.Max()vs OrderByDescending()。First()

这纯粹是出于我自己的知识,如果我要编写我将使用的代码.Max()

一开始以为.Max()只需要通过numbers来传递numbers来查找最大值,而第二种方法必须对可枚举的整个事物进行排序然后找到第一个。 所以它是O(n) vs O(n lg n) 。 但后来我想也许它知道它只需要最高,只是抓住它。

问题: LINQ和/或编译器是否足够智能,以确定它不需要对整个可枚举进行排序,并将代码缩小到与.Max()基本相同的位置? 有一种可量化的方法可以找到答案吗?

 IEnumerable numbers = Enumerable.Range(1, 1000); int max = numbers.Max(); int max2 = numbers.OrderByDescending(x => x).First(); 

如果你正在谈论直接LINQ to Objects,那么不,它没有为此进行优化。

可能是另一个LINQ提供商可以做到的,但这取决于实现的细节。

对于Enumerable,Reflector给我的实现是:

 public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector) { return new OrderedEnumerable(source, keySelector, null, false); } 

并为First()

 public static TSource First(this IEnumerable source) { if (source == null) { throw Error.ArgumentNull("source"); } IList list = source as IList; if (list != null) { if (list.Count > 0) { return list[0]; } } else { using (IEnumerator enumerator = source.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } } } throw Error.NoElements(); } 

LINQ和/或编译器是否足够聪明,可以确定它不需要对整个可枚举进行排序,并将代码缩小到与.Max()基本相同的程度?

没有。

有一种可量化的方法可以找到答案吗?

使用秒表的简单基准:

  var numbers = Enumerable.Range(1, 10000000); var sw = Stopwatch.StartNew(); int max = numbers.Max(); Console.WriteLine(sw.ElapsedMilliseconds); sw.Restart(); int max2 = numbers.OrderByDescending(x => x).First(); Console.WriteLine(sw.ElapsedMilliseconds); 

Max():70ms

OrderBy():2066ms

此外,如果你将计数增加到太多,则OrderBy()会因OutOfMemoryException而失败,而Max()则不会。

.Max()是O(n),而OrderByDescending解决方案不是 – 根据排序,它可能是O(nlog(n))。

我显然没有在编译器内部挖掘知道,但你要求的是(实现排序的优化然后只抓取一个项目与.max相同)在编译器中相当多。

似乎OrderedEnumerable现在足够聪明,可以发现它不需要为First和Last()排序列表。

请注意第223行附近的代码,TryGetFirst() https://github.com/dotnet/corefx/blob/ed0ee133ac49cee86f10ca4692b1d72e337bc012/src/System.Linq/src/System/Linq/OrderedEnumerable.cs