C# 优化自然排序序列上的连接、分组、不同、最小/最大

C# 优化自然排序序列上的连接、分组、不同、最小/最大,c#,.net,linq-to-objects,C#,.net,Linq To Objects,LINQ运算符在输入序列未排序的假设下运行,这对于一般情况非常有用。但是,如果根据键值对源序列进行排序,则上述运算符可能更有效 例如,Join将整个内部序列读取到哈希表中,然后只在外部序列上迭代。如果对两个序列进行了排序,那么Join可以实现为一个简单的合并,而不需要额外的存储和哈希表查找 是否有一个库具有在预排序序列上运行的替代高性能LINQ函数?是,但不适用于LINQ to对象。大多数在IQueryable上工作的LINQ提供程序已经将其转换为“本机”函数,可以轻松地进行这种类型的优化。例如

LINQ运算符在输入序列未排序的假设下运行,这对于一般情况非常有用。但是,如果根据键值对源序列进行排序,则上述运算符可能更有效

例如,Join将整个内部序列读取到哈希表中,然后只在外部序列上迭代。如果对两个序列进行了排序,那么Join可以实现为一个简单的合并,而不需要额外的存储和哈希表查找


是否有一个库具有在预排序序列上运行的替代高性能LINQ函数?

是,但不适用于LINQ to对象。大多数在
IQueryable
上工作的LINQ提供程序已经将其转换为“本机”函数,可以轻松地进行这种类型的优化。例如,当使用实体框架时,EF提供程序将把它转换为SQL调用,DB(希望)将正确地对此进行优化

不过,LINQ到对象有点不同。在那里,大多数例程(包括上述所有例程)设计用于处理未排序的数据,甚至是
IEqualityComparer
IComparer
的不同实现。这意味着“优化”版本不仅可以处理一小部分潜在数据,而且只能针对标准查询操作的子集进行优化


也就是说,对于那些特定的情况,使用您自己的包装器围绕标准LINQ操作来实现这一点是相当容易的。但是,您需要一种方法来提前知道所讨论的集合已排序,这可能需要您自己的单独接口(或运行时检查,例如对
ICollection
进行的
Count()
优化)。

如Reed所述,为了确定优化是否有效,很难发现序列是按什么排序的。您真的不希望必须滚动重复的集合类,或者将自己绑定到特定的实现(如
IOrderedEnumerable
)来编写LINQ扩展方法的重写

那么,仅仅添加一些新的操作符或重载怎么样?作为消费者,您可以保证数据是有序的。这些方法仍然可以是
IEnumerable
上的扩展方法,除非订购集合,否则不能保证成功

例如,
OrderedJoin
,您必须提供一个
i可比较的
,以便知道如果每个序列中的当前项在键上没有匹配项,下一步前进哪个序列。这是我的签名作为开场白。您可以在实现整个库后通知我们

public static IEnumerable<TResult> OrderedJoin<TOuter, TInner, TKey, TResult>(
    this IEnumerable<TOuter> outer,
    IEnumerable<TInner> inner,
    Func<TOuter, TKey> outerKeySelector,
    Func<TInner, TKey> innerKeySelector,
    Func<TOuter, TInner, TResult> resultSelector,
    IComparable<TKey> comparer
)
公共静态IEnumerable OrderedJoin(
这是数不清的外部,
我可数的内心世界,
Func outerKeySelector,
Func innerKeySelector,
Func结果选择器,
可比比较器
)
我有时间就开发。它提供了
ISortedEnumerable
ISortedList
以及您建议的一些优化。我还包括了更具争议性的优化(例如,
Skip
for
IList
,这稍微改变了语义); 使用系统集合; 使用System.Collections.Generic; 使用System.Linq; 命名空间有序联接 { 公共静态类EnumerableExtension { 私有枚举类型 { 内部的 左边 正确的, 满满的 } 私有静态IEnumerable OrderedJoinIterator( 此IEnumerable left、IEnumerable right、Func结果选择器、JoinType jt、IComparer比较器) { 如果(left==null)抛出新的ArgumentNullException(“left”); 如果(right==null)抛出新的ArgumentNullException(“right”); 如果(resultSelector==null)抛出新的ArgumentNullException(“resultSelector”); if(比较器==null) comparer=comparer.Default; var l=left.GetEnumerator(); var r=right.GetEnumerator(); var lHasData=l.MoveNext(); var rHasData=r.MoveNext(); while(拉斯达| |拉斯达) { 如果(!拉斯达&拉斯达) { if(jt==JoinType.Inner | | jt==JoinType.Left) 屈服断裂; 收益率返回结果选择器(默认值(T),r.Current); rHasData=r.MoveNext(); 继续; } 如果(!rHasData&&lHasData) { if(jt==JoinType.Inner | | jt==JoinType.Right) 屈服断裂; 收益率返回结果选择器(l.当前值,默认值(T)); lHasData=l.MoveNext(); 继续; } var comp=比较器比较(l电流,r电流); if(comp<0) { if(jt==JoinType.Left | | jt==JoinType.Full) 收益率返回结果选择器(l.当前值,默认值(T)); lHasData=l.MoveNext(); } 否则如果(补偿>0) { if(jt==JoinType.Right | | jt==JoinType.Full) 收益率返回结果选择器(默认值(T),r.Current); rHasData=r.MoveNext(); } 其他的 { 收益返回结果选择器(l.Current,r.Current); lHasData=l.MoveNext(); rHasData=r.MoveNext(); } } } 公共静态IEnumerable OrderedInnerJoin( 此IEnumerable left、IEnumerable right、Func resultSelector、IComparer comparer comparer=null) { 返回OrderedJoinIterator(左、右、结果集)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace OrderedJoin
{
    public static class EnumerableExtension
    {
        private enum JoinType
        {
            Inner,
            Left,
            Right,
            Full
        }

        private static IEnumerable<TResult> OrderedJoinIterator<T, TResult>(
            this IEnumerable<T> left, IEnumerable<T> right, Func<T, T, TResult> resultSelector, JoinType jt, IComparer<T> comparer)
        {
            if (left == null) throw new ArgumentNullException("left");
            if (right == null) throw new ArgumentNullException("right");
            if (resultSelector == null) throw new ArgumentNullException("resultSelector");

            if (comparer == null)
                comparer = Comparer<T>.Default;

            var l = left.GetEnumerator();
            var r = right.GetEnumerator();

            var lHasData = l.MoveNext();
            var rHasData = r.MoveNext();

            while (lHasData || rHasData)
            {
                if (!lHasData && rHasData)
                {
                    if (jt == JoinType.Inner || jt == JoinType.Left)
                        yield break;
                    yield return resultSelector(default(T), r.Current);
                    rHasData = r.MoveNext();
                    continue;
                }
                if (!rHasData && lHasData)
                {
                    if (jt == JoinType.Inner || jt == JoinType.Right)
                        yield break;
                    yield return resultSelector(l.Current, default(T));
                    lHasData = l.MoveNext();
                    continue;
                }

                var comp = comparer.Compare(l.Current, r.Current);

                if (comp < 0)
                {
                    if (jt == JoinType.Left || jt == JoinType.Full)
                        yield return resultSelector(l.Current, default(T));
                    lHasData = l.MoveNext();
                }
                else if (comp > 0)
                {
                    if (jt == JoinType.Right || jt == JoinType.Full)
                        yield return resultSelector(default(T), r.Current);
                    rHasData = r.MoveNext();
                }
                else
                {
                    yield return resultSelector(l.Current, r.Current);
                    lHasData = l.MoveNext();
                    rHasData = r.MoveNext();
                }
            }
        }

        public static IEnumerable<TResult> OrderedInnerJoin<T, TResult>(
            this IEnumerable<T> left, IEnumerable<T> right, Func<T, T, TResult> resultSelector, IComparer<T> comparer = null)
        {
            return OrderedJoinIterator(left, right, resultSelector, JoinType.Inner, comparer);
        }

        public static IEnumerable<TResult> OrderedFullJoin<T, TResult>(
            this IEnumerable<T> left, IEnumerable<T> right, Func<T, T, TResult> resultSelector, IComparer<T> comparer = null)
        {
            return OrderedJoinIterator(left, right, resultSelector, JoinType.Full, comparer);
        }

        public static IEnumerable<TResult> OrderedLeftJoin<T, TResult>(
            this IEnumerable<T> left, IEnumerable<T> right, Func<T, T, TResult> resultSelector, IComparer<T> comparer = null)
        {
            return OrderedJoinIterator(left, right, resultSelector, JoinType.Left, comparer);
        }

        public static IEnumerable<TResult> OrderedRightJoin<T, TResult>(
            this IEnumerable<T> left, IEnumerable<T> right, Func<T, T, TResult> resultSelector, IComparer<T> comparer = null)
        {
            return OrderedJoinIterator(left, right, resultSelector, JoinType.Right, comparer);
        }
    }

    internal class TestEnum : IEnumerable<int>
    {
        public TestEnum(string name, IList<int> nums)
        {
            Name = name;
            Nums = nums;
        }

        public string Name { get; private set; }
        public IList<int> Nums { get; private set; }

        public IEnumerator<int> GetEnumerator()
        {
            foreach (var item in Nums)
            {
                Console.WriteLine("{0}: {1}", Name, item);
                yield return item;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var e1 = new TestEnum("L", new List<int> { 1, 2, 5, 6 });
            var e2 = new TestEnum("R", new List<int> { 1, 3, 4, 6 });

            var print = new Action<IEnumerable<string>>(seq => { foreach (var item in seq) Console.WriteLine("\t" + item); });

            Console.WriteLine("Standard Inner Join:");
            print(e1.Join(e2, i => i, j => j, (i, j) => string.Format("{0} <=> {1}", i, j), EqualityComparer<int>.Default));

            Console.WriteLine("Ordered Inner Join:");
            print(e1.OrderedInnerJoin(e2, (i, j) => string.Format("{0} <=> {1}", i, j)));

            Console.WriteLine("Ordered Full Join:");
            print(e1.OrderedFullJoin(e2, (i, j) => string.Format("{0} <=> {1}", i, j)));

            Console.WriteLine("Ordered Left Join:");
            print(e1.OrderedLeftJoin(e2, (i, j) => string.Format("{0} <=> {1}", i, j)));

            Console.WriteLine("Ordered Right Join:");
            print(e1.OrderedRightJoin(e2, (i, j) => string.Format("{0} <=> {1}", i, j)));

            Console.ReadLine();
        }
    }
}