LINQ运算符的运算假设输入序列没有排序,这对于一般情况很有用。但是,如果源序列按键值排序,则上述运算符可能更有效。
例如,Join将整个内部序列读入哈希表,然后迭代外部序列。如果对两个序列进行了排序,则可以将Join实现为简单合并,而无需额外的存储和哈希表查找。
是否有一个库具有在预先排序的序列上运行的替代高性能LINQ函数?
答案 0 :(得分:0)
是的,但不适用于LINQ to Objects。在IQueryable<T>
上工作的大多数LINQ提供程序已经对“本机”函数进行了转换,可以很容易地进行这种类型的优化。例如,在使用Entity Framework时,EF提供程序会将其转换为SQL调用,并且DB(希望)会正确地优化它。
LINQ to Objects虽然有点不同。在那里,大多数例程(包括上述所有例程)都设计用于处理未排序的数据,甚至是IEqualityComparer<T>
或IComparer<T>
的不同实现。这意味着“优化”版本不仅适用于一小部分潜在数据,而且仅针对标准查询操作的子集进行优化。
话虽如此,对于那些特定情况,使用围绕标准LINQ操作的自己的包装器来执行此操作相当容易。您需要提前知道有问题的集合是如何排序的,然而,这可能需要您自己的单独接口(或运行时检查,例如Count()
上的ICollection
优化})。
答案 1 :(得分:0)
正如里德所提到的那样,很难发现序列的排序方式,以确定优化是否有效。您并不是真的想要滚动重复的集合类,或者将自己绑定到特定的实现(如IOrderedEnumerable<T>
),以编写LINQ扩展方法的覆盖。
那么,只需添加一些新的运算符或重载,您作为消费者可以保证数据的排序。这些仍然可以是IEnumerable<T>
上的扩展方法,除非订购了该集合,否则无法保证成功。
一个示例是OrderedJoin
,如果每个序列中的当前项目与键。这是作为首发的签名。您可以在实施整个图书馆时通知我们!
IComparable<TKey>
答案 2 :(得分:0)
我有时间开发Nito.LINQ。它为ISortedEnumerable
和ISortedList
提供了您建议的一些优化。我还包括更多有争议的优化(例如,Skip
IList
,这稍微改变了语义。)
答案 3 :(得分:0)
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();
}
}
}