这是一个主观的问题,但让我尽力回答这个问题(从 CLR 2.0 的角度来看。只是因为我还没有探索 CLR 4.0 的字典是否有任何变化)。
您正在使用以字符串为键的字典。由于可能存在无限个可能的字符串,因此可以合理地假设每个可能的哈希码“均等”。或者换句话说,每个 2^32 哈希码(int 范围)都同样可能用于字符串类。BCL 中的当前版本的字典从由此获得的任何 32 位哈希码中删除第 32 位,从而基本上获得 31 位哈希码。因此,我们正在处理的范围是 2^31 个唯一的等可能哈希码。
请注意,哈希码的范围不取决于字典包含或可以包含的元素数量。
字典类将使用此哈希码将存储桶分配给“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 实例的数量,因为在任何情况下字典都需要这么多容量来存储实例,但这不会最小化“哈希冲突”,并且在正常情况下将是足够快。