【发布时间】:2019-06-01 08:59:22
【问题描述】:
前几天我正在阅读that article on CodeProject
我很难理解关于 .NET Dictionary 实现的几点(考虑到实现 here 没有在 .NET Core 中进行所有优化):
注意:如果会添加超过表格中的最大数量的项目 (即 7199369),resize 方法会手动搜索下一个素数 大于旧大小两倍的数字。
注意:在调整大小时尺寸加倍的原因 数组是使内部哈希表操作具有渐近性 复杂。素数被用来支持 双重哈希。
所以我试着回忆十年前我和我的好朋友维基百科上的旧 CS 课程:
但是我仍然没有真正看到它首先与双散列(这是一种针对开放寻址散列表的冲突解决技术)的关系,除了 Resize() 方法是 entries 的双倍基于最小素数(基于当前/旧大小),而且我真的没有看到“加倍”大小的好处,“渐近复杂度”(我猜那篇文章的意思是 O(n) 当底层数组(条目)已满,可调整大小)。
首先,如果使用或不使用素数将大小加倍,它真的不一样吗?
其次,在我看来,.NET 哈希表在解决冲突时使用了单独的链接技术。
我想我一定错过了一些事情,我希望有人能够阐明这两点。
【问题讨论】:
-
加倍分配大小避免 O(n) 重新分配成本,它减少到 O(log2(n))。除 LinkedList 之外的所有 .NET 容器类都使用它。来自自然的this example 很好地解释了为什么舍入到素数是有用的。
-
我不认为 .NET 使用双重哈希,因为计算哈希码的工作委托给对象本身(至少在默认情况下),你不能问这些对象使用不同的素数或不同的散列算法。
-
当前的实现(至少通过reference source 是字典维护一个“空闲桶列表”,并在发生冲突时通过将它们与现有桶链接在一起来重用它们。如果没有空闲槽可用,但字典未满,然后使用下一个可用条目。如果没有可用的空闲插槽,并且字典被认为已满,则调整其大小。
-
此外,字符串得到特殊处理。当字符串上发生太多冲突时,就会使用一个新的随机字符串比较器(我不知道这里的“随机”这个词是什么意思确切地,但这是内部类名的一部分)。我假设这是为了处理您添加大量字符串的情况,这些字符串由于某种原因最终导致大量冲突,然后创建一个新的比较器,它将(希望)在新字典中分配具有较少冲突的字符串。然后使用这个新的比较器重新分配条目。
-
然而,这一切都是实现细节。您唯一可以依赖的是它的行为与记录一致,而这些都没有记录。
标签: c# .net dictionary