我最近开始大量使用LINQ,但我没有看到任何一种LINQ方法的运行时复杂性.显然,这里有很多因素在起作用,所以让我们把讨论限制在普通的IEnumerable LINQ对象提供程序上.此外,让我们假设作为 Select 器/变异器等传入的任何Func都是廉价的O(1)操作.

显然,所有单次通过操作(SelectWhereCountTake/SkipAny/All等)将是O(N),因为它们只需要遍历序列一次;尽管即使这样也会受到懒惰的影响.

对于更复杂的操作来说,情况更加模糊;set-like操作符(UnionDistinctExcept等)在默认情况下使用GetHashCode(afaik),因此假设它们在内部使用哈希表似乎是合理的,一般来说,这些操作也是O(n).那么使用IEqualityComparer的版本呢?

OrderBy需要排序,所以我们很可能看到的是O(n logn).如果已经分类了呢?如果我说OrderBy().ThenBy(),并为两者提供相同的密钥,怎么样?

我可以看到GroupBy个(和Join个)使用排序或散列.到底是哪一个?

ContainsList上是O(n),但在HashSet上是O(1)——LINQ是否判断底层容器以查看它是否可以加快速度?

而真正的问题是——到目前为止,我一直相信这些行动是有效的.然而,我能指望吗?例如,STL容器明确规定了每个操作的复杂性.是否有类似的LINQ性能保证.NET库规范?

更多问题(回复 comments ):
我没有真正考虑过开销,但我没想到会有太多简单的Linq-to-Objects.CodingHorror的帖子谈到了Linq-to-SQL,在那里我可以理解解析查询和创建SQL会增加成本-对象提供者也有类似的成本吗?如果是这样,那么使用声明性语法还是函数语法会有什么不同呢?

推荐答案

有非常非常少的保证,但有一些优化:

  • 使用索引访问的扩展方法,例如ElementAtSkipLastLastOrDefault,将判断底层类型是否实现了IList<T>,以便获得O(1)访问,而不是O(N).

  • Count方法判断ICollection实现,因此该操作是O(1)而不是O(N).

  • DistinctGroupByJoin,我相信集合聚合方法(UnionIntersectExcept)也使用散列,所以它们应该接近O(N),而不是O(N²).

  • Contains判断ICollection实现,因此如果基础集合也是O(1),例如HashSet<T>,则may应该是O(1),但这取决于实际的数据 struct ,不能保证.哈希集覆盖Contains方法,这就是为什么它们是O(1).

  • OrderBy个方法使用稳定的快速排序,因此它们是O(N Log N)平均情况.

我认为这涵盖了大部分(如果不是全部的话)内置扩展方法.几乎没有性能保证;Linq本身将try 利用高效的数据 struct ,但编写可能效率低下的代码并非易事.

.net相关问答推荐

升级到.NET8后,SignalR(在坞站容器上)网关损坏

.NET MAUI ListView - ObservableCollection - 在异步方法期间不更新

仅使用 .NET GetBytes 方法转换有效字节而不创建问号

EGC / 文本元素上的 .NET String.Split

在 Rx 中,处理线程安全是否是消费者(IObserver)的责任?

Gacutil.exe 成功添加程序集,但在资源管理器中无法查看程序集.为什么?

根源是什么?

如何获取 Sql Server 数据库中所有模式的列表

使用 Windows 服务和 C# 检测 USB 驱动器插入和移除

如何使用c#从excel文件中读取数据

DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") 返回上午时间而不是下午时间?

C# 相当于 Java 的 <?在泛型中扩展 Base>

关闭 Visual Studio 中所有选项卡但当前选项卡的键盘快捷键?

互锁且易变

使用 XmlSerializer 将空 xml 属性值反序列化为可为空的 int 属性

如何对 LINQ to XML 中的元素进行深层复制?

风格上的差异:IDictionary vs Dictionary

.NET 高级别的 .NET 4.0 和 .NET 4.5 的区别

有没有一种简单的方法来判断 .NET Framework 版本?

C#/.NET 中仅命名空间的类可见性?