为什么HashSet<T>class不是用来实现Enumerable.Distinct
Why HashSet<T> class is not used to implement Enumerable.Distinct
我需要访问渐近时间和 space 大 O 表示法 IEnumerable.Distinct
的复杂度
所以我在看扩展方法 Enumerable.Distinct
and I see it is implemented using and internal class Set<T>
的实现,这几乎是散列 table 和 "open addressing"
的经典实现
很快就吸引眼球的是 Set<T>
is just a copy-paste from HashSet<T>
中有很多代码,还有一些遗漏
然而,这简化了 Set<T>
implementation has some obvious flaws, for example the Resize
method not using prime numbers for the size of the slots, like HashSet<T>
does, see HashHelpers.ExpandPrime
所以,我的问题是:
- 这里代码重复的原因是什么,为什么不坚持DRY原则呢?特别是考虑到这两个 类 都在同一个程序集中
System.Core
- 看起来像
HashSet<T>
will perform better, so should I avoid using Distinct extension method, and write my own extension method that would use HashSet<T>
instead of Set<T>
?
which is almost a classical implementation of a hash table with "open addressing"
再看看。它与列表头单元格分开链接。虽然插槽都在一个数组中,但在发生冲突的情况下查找下一个插槽是通过检查当前插槽的 next
字段来完成的。这比使用每个节点作为单独堆对象的链表具有更好的缓存效率,尽管在这方面不如开放式寻址。同时也避免了一些开放寻址效果不好的情况。
a lot of code in Set is just a copy-paste from HashSet, with some omissions
AFAICT 使用 hash-set 的私有实现的原因是 Enumerable
和 HashSet
大约在同一时间独立开发。这只是我的推测,但它们都是在 .NET 3.5 中引入的,所以它是可行的。
很可能 HashSet<T>
是从复制 Set<T>
开始的,然后让它更好地服务于公开展示,尽管也有可能两者都基于相同的独立链接原则列出头部单元格
在性能方面,HashSet
使用质数意味着它更有可能避免与不良哈希值的冲突(但究竟有多大优势,这不是一个简单的问题),但是Set
在很多方面都更轻巧,尤其是在 .NET Core 中,其中删除了一些不需要的东西。特别是,Set
的那个版本利用了一个事实,即一旦一个项目被删除(例如,在 Intersect
期间发生),就永远不会添加一个项目,这允许它遗漏freelist
以及与之相关的任何工作,HashSet
无法完成。即使是初始实施也更轻松,因为不跟踪版本以在枚举期间捕获更改,这是一个很小的成本,但是每次添加和删除都会有成本。
因此,对于具有不同散列码分布的不同数据集,有时一个表现更好,有时另一个表现更好。
Especially given the fact that both of these classes are in the same assembly System.Core
仅在某些版本的 .NET 中,在某些版本中它们位于单独的程序集中。在 .NET Core 中,我们有两个版本的 Set<T>
,一个在具有 System.Linq
的程序集中,另一个在具有 System.Linq.Expressions
的单独程序集中。前者如上所述被削减,后者被 HashSet<T>
取代,因为它在那里做得更少。
当然 System.Core 排在第一位,但这些元素完全可以分离出来的事实说明 System.Core 不是 inter-dependencies.[=40 的单一整体块=]
.NET Core 版本的 Linq 中现在有一个 ToHashSet()
方法,这使得用 HashSet<T>
替换 Set<T>
的可能性更加合理,尽管不是 no-brainer .我认为@james-ko 正在考虑测试这样做的好处。
It looks like HashSet<T>
will perform better
出于上述原因,情况可能并非如此,但可能确实如此,具体取决于源数据。那是在考虑跨几种不同 linq 方法的优化之前(在 linq 的初始版本中不多,但在 .NET Core 中有很多)。
so should I avoid using Distinct
extension method, and write my own extension method that would use HashSet<T>
instead of Set<T>
.
使用Distinct()
。如果您遇到瓶颈,那么 HashSet<T>
可能会在给定的 data-set 下获胜,但如果您尝试这样做,请确保您的分析与您的代码在现实生活中遇到的实际值非常匹配。如果您的应用程序遇到另一种方法做得更好的情况,那么根据一些任意测试来决定一种方法更快是没有意义的。 (如果我发现这是一个问题点,我会先看看所讨论的类型的 GetHashCode()
是否可以改进速度或位分布)。
我需要访问渐近时间和 space 大 O 表示法 IEnumerable.Distinct
的复杂度
所以我在看扩展方法 Enumerable.Distinct
and I see it is implemented using and internal class Set<T>
的实现,这几乎是散列 table 和 "open addressing"
很快就吸引眼球的是 Set<T>
is just a copy-paste from HashSet<T>
中有很多代码,还有一些遗漏
然而,这简化了 Set<T>
implementation has some obvious flaws, for example the Resize
method not using prime numbers for the size of the slots, like HashSet<T>
does, see HashHelpers.ExpandPrime
所以,我的问题是:
- 这里代码重复的原因是什么,为什么不坚持DRY原则呢?特别是考虑到这两个 类 都在同一个程序集中
System.Core
- 看起来像
HashSet<T>
will perform better, so should I avoid using Distinct extension method, and write my own extension method that would useHashSet<T>
instead ofSet<T>
?
which is almost a classical implementation of a hash table with "open addressing"
再看看。它与列表头单元格分开链接。虽然插槽都在一个数组中,但在发生冲突的情况下查找下一个插槽是通过检查当前插槽的 next
字段来完成的。这比使用每个节点作为单独堆对象的链表具有更好的缓存效率,尽管在这方面不如开放式寻址。同时也避免了一些开放寻址效果不好的情况。
a lot of code in Set is just a copy-paste from HashSet, with some omissions
AFAICT 使用 hash-set 的私有实现的原因是 Enumerable
和 HashSet
大约在同一时间独立开发。这只是我的推测,但它们都是在 .NET 3.5 中引入的,所以它是可行的。
很可能 HashSet<T>
是从复制 Set<T>
开始的,然后让它更好地服务于公开展示,尽管也有可能两者都基于相同的独立链接原则列出头部单元格
在性能方面,HashSet
使用质数意味着它更有可能避免与不良哈希值的冲突(但究竟有多大优势,这不是一个简单的问题),但是Set
在很多方面都更轻巧,尤其是在 .NET Core 中,其中删除了一些不需要的东西。特别是,Set
的那个版本利用了一个事实,即一旦一个项目被删除(例如,在 Intersect
期间发生),就永远不会添加一个项目,这允许它遗漏freelist
以及与之相关的任何工作,HashSet
无法完成。即使是初始实施也更轻松,因为不跟踪版本以在枚举期间捕获更改,这是一个很小的成本,但是每次添加和删除都会有成本。
因此,对于具有不同散列码分布的不同数据集,有时一个表现更好,有时另一个表现更好。
Especially given the fact that both of these classes are in the same assembly System.Core
仅在某些版本的 .NET 中,在某些版本中它们位于单独的程序集中。在 .NET Core 中,我们有两个版本的 Set<T>
,一个在具有 System.Linq
的程序集中,另一个在具有 System.Linq.Expressions
的单独程序集中。前者如上所述被削减,后者被 HashSet<T>
取代,因为它在那里做得更少。
当然 System.Core 排在第一位,但这些元素完全可以分离出来的事实说明 System.Core 不是 inter-dependencies.[=40 的单一整体块=]
.NET Core 版本的 Linq 中现在有一个 ToHashSet()
方法,这使得用 HashSet<T>
替换 Set<T>
的可能性更加合理,尽管不是 no-brainer .我认为@james-ko 正在考虑测试这样做的好处。
It looks like
HashSet<T>
will perform better
出于上述原因,情况可能并非如此,但可能确实如此,具体取决于源数据。那是在考虑跨几种不同 linq 方法的优化之前(在 linq 的初始版本中不多,但在 .NET Core 中有很多)。
so should I avoid using
Distinct
extension method, and write my own extension method that would useHashSet<T>
instead ofSet<T>
.
使用Distinct()
。如果您遇到瓶颈,那么 HashSet<T>
可能会在给定的 data-set 下获胜,但如果您尝试这样做,请确保您的分析与您的代码在现实生活中遇到的实际值非常匹配。如果您的应用程序遇到另一种方法做得更好的情况,那么根据一些任意测试来决定一种方法更快是没有意义的。 (如果我发现这是一个问题点,我会先看看所讨论的类型的 GetHashCode()
是否可以改进速度或位分布)。