静态哈希表应该有多少容量来最小化冲突?

时间:2012-07-04 05:06:29

标签: c# .net hashtable

我的程序通过字符串ID检索我想要引用的有限且完整的元素列表。我正在使用.Net Dictionary<string, MyClass>来存储这些元素。我个人不知道会有多少元素。它可能是一些。它可能是成千上万。

鉴于程序确切地知道它将在哈希表中放入多少元素,它应该指定什么作为表的容量。显然它至少应该包含它所包含的元素数量,但仅使用该数字可能会导致大量碰撞。

是否有指南为已知数量的元素选择哈希表的容量以平衡哈希冲突和内存浪费?

编辑:我知道哈希表的大小可以改变。我首先要避免的是使用默认分配,然后立即添加数千个元素,导致无数的调整大小操作。一旦填充了元素,我就不会添加或删除元素。如果我知道发生了什么,我可以确保前期有足够的空间。我的问题涉及哈希冲突与内存浪费的平衡。

3 个答案:

答案 0 :(得分:3)

你的问题似乎暗示了一个错误的假设,即字典的容量是固定的。事实并非如此。

如果您知道在任何给定的情况下字典将至少包含一些元素,那么您可以将该数字指定为字典的初始容量。字典的容量始终至少与其项目数一样大(至少在.NET 2到4中都是如此;我相信这是一个未经文档化的实现细节,可能会发生变化)。

通过消除字典从其默认初始容量增长到您选择的容量时发生的内容,指定初始容量可以减少内存分配的数量。

如果选择使用的哈希函数,则冲突的数量应该相对较小,并且应该对性能的影响最小。指定一个过大的容量可能有助于一些人为的情况,但我绝对不会考虑任何想法,除非分析显示字典的查找对性能有重大影响。

(作为一个设计情况的例子,考虑一个容量为10007的int密钥的字典,其所有密钥都是10007的倍数。使用当前实现,所有项目都将被存储在一个桶中,因为通过将哈希码除以容量并取余数来选择桶。在这种情况下,字典将用作链表,并且强制它使用不同的容量将解决这个问题。)< / p>

答案 1 :(得分:2)

这是一个主观问题,但让我尽力回答这个问题(从CLR 2.0的角度来看。只是因为我还没有探究过CLR 4.0的字典是否有任何变化)。

您正在使用键入字符串的词典。由于可能存在无限可能的字符串,因此可以合理地假设每个可能的哈希码都“同样可能”。或者换句话说,2 ^ 32个哈希码(int的范围)中的每一个对于字符串类同样可能。 BCL中当前版本的Dictionary从如此获得的任何32位哈希码中删除第32位,基本上获得31位哈希码。因此,我们处理的范围是2 ^ 31个唯一可能的哈希码。

请注意,哈希码的范围不依赖于词典包含或可包含的元素数量。

Dictionary类将使用此哈希代码将桶分配给“Myclass”对象。所以基本上如果两个不同的字符串返回相同的31位哈希码(假设BCL设计者非常明智地选择了字符串哈希函数,这样的实例应该相当分散)两者都将被分配相同的桶。在这种哈希冲突中,什么都做不了。

现在,在Dictionary类的当前实现中,可能会发生甚至不同的哈希码(同样是31位)仍然在同一个桶中。桶索引标识如下:

hash = <31 bit hash code>
pr = <least prime number greater than or equal to current dictionary capacity>
bucket_index = hash modulus pr

因此,形式(pr * factor + bucket_index)的每个哈希码都将在同一个桶中结束,而不管因素部分。

如果你想绝对确定所有不同的可能的31位哈希码最终在不同的桶中,那么只能强制pr大于或等于最大可能的31位哈希码。或者换句话说,确保每个哈希码的形式(pr * 0 + hash_code),即pr应该大于2 ^ 31。这通过扩展意味着字典容量应至少为2 ^ 31。

请注意,最小化哈希冲突所需的容量完全不取决于您希望存储在字典中的元素数量,而是取决于可能的哈希码的范围。

你可以想象2 ^ 31是巨大的内存分配。实际上,如果您尝试指定2 ^ 31作为容量,则将有两个长度为2 ^ 31的数组。考虑到在32位机器上,RAM上最高可能的地址是2 ^ 32 !!!!!

如果出于某种原因,您无法接受字典的默认行为,并且最小化哈希冲突(或者说我会说桶冲突)对您来说至关重要,只希望您提供自己的哈希码(即你不能使用字符串作为键)。这样的哈希码应该保持公式以获得桶索引并努力最小化可能的哈希码的范围。最简单的方法是逐步为您的唯一MyClass实例分配一个数字/索引,并使用此数字作为哈希代码。然后,您可以将MyClass实例的总数指定为字典容量。但是,在这种情况下,可以很容易地维护数组而不是字典,因为您知道对象和索引的“索引”是递增的。

最后,我想重复其他人所说的话,“不会有无数的调整大小”。每当字典发现自身空间不足时,它的容量就会翻倍(四舍五入到大于或等于新容量的最接近的素数)。为了节省一些处理,您可以很好地设置您拥有的MyClass实例数量的容量,因为在任何情况下,字典都需要这么多容量来存储实例,但这不会最小化“哈希冲突”,并且对于正常情况将是足够快。

答案 2 :(得分:1)

像HashTable这样的数据结构用于动态内存分配。但是,您可以在某些结构中提及初始尺寸。但是,当您添加新元素时,它们的大小会扩大。绝对不能隐含地限制大小。

有许多可用的数据结构,各有利弊。你需要选择最好的一个。限制大小不会影响性能。您需要注意添加,删除和搜索,这会影响性能。