【问题标题】:Why is C# OrderBy so fast? [duplicate]为什么 C# OrderBy 这么快? [复制]
【发布时间】:2020-03-05 18:49:20
【问题描述】:

看看这个例子:https://repl.it/repls/PuzzledAthleticMacroinstruction

它创建一个包含随机值的对象列表,然后对它们进行排序。根据 n*log(n) 的大 O,您会期望排序花费的时间最长。但是,排序比生成列表要快得多。

输出是:

Generate list in 8421 milliseconds
Sort in 46 milliseconds
Before sorting, first element was 8445383 and last element was 11537420
After sorting, first element is 0 and last element is 11999999

这似乎是不可能的。

【问题讨论】:

  • 因为它是懒惰的。如果您不需要单个列表,则它根本不会进行任何排序。如果您只对第一个元素感兴趣,则需要 O(n).
  • @WillemVanOnsem 示例中的代码确实访问了第一个和最后一个元素。列表不需要为此排序吗?
  • 你真的应该在问题中包含代码,而不仅仅是在链接中
  • 添加到 Willem Van Onsem 的富有洞察力的评论中,如果您在调用中添加 .ToList(),它会突然开始花费一些时间。
  • @TomDane:是的,但不是在.OrderBy(..) 本身。只有当您调用 .Last() 时,它才会真正“排序”传递给它的元素。

标签: c# algorithm performance linq sorting


【解决方案1】:

为什么 C# OrderBy 这么快?

因为它实际上并没有对集合进行排序。它将排序操作添加到表达式树并返回该表达式树以供以后可能的评估。然后,您可以将进一步的操作附加到该表达式树,并且在您从中读取数据之前不会对其进行评估。

考虑一下这些 LINQ 扩展方法最常见的用途可能是什么……查询数据库。您不希望为添加到树中的每个操作一遍又一遍地查询数据库。毕竟,如果其中一个操作是.Where(),这会大大减少结果的数量。将 所有 数据具体化到内存中,执行 N 次操作,最终还是最终过滤掉数据是很愚蠢的。相反,如果需要实际数据,则构建逻辑表达式树,然后将其转换为针对数据库的大型(但已优化)查询。

同样的事情也发生在您的代码中。事实上,细心的观察者在运行您的代码时会注意到最后两条语句之间的时间相对较长(至少是人类可观察到的)。

Console.WriteLine($"Before sorting, first element was {unsorted.First().Foo} and last element was {unsorted.Last().Foo}");

Console.WriteLine($"After sorting, first element is {sorted.First().Foo} and last element is {sorted.Last().Foo}");

这意味着执行第二条语句需要相当长的时间。第二条语句似乎所做的只是从集合中读取一个值。 然而,由于它是从sorted 集合中读取的第一个操作,是在评估表达式树时以及最终在评估集合时已排序。

如果您想在调用.OrderBy() 时强制执行排序操作,实现整个集合的最简单方法是附加.ToList()

var sorted = unsorted.OrderBy(element => element.Foo).ThenBy(element => element.Bar).ToList();

这样做你会发现“排序”操作现在需要人类可观察到的时间量,而输出元素的最后两条语句根本不需要时间。

【讨论】:

  • 感谢您抽出宝贵时间回答@David。抱歉问题已被版主删除。
  • @TomDane 这个问题没有被删除。它被标记为重复。问题将继续存在。
猜你喜欢
  • 2017-08-28
  • 2016-12-14
  • 2014-04-11
  • 1970-01-01
  • 2016-07-03
  • 2013-08-04
  • 2020-03-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多