不确定它是否合理地重新打开了Hashing URL的早期主题。 尽管如此,我仍然很想知道这项工作是如何进行的。
假设:我们有一个哈希表,其中n(其中n< Infinity)元素的渐近时间复杂度为o(1);我们(CLR)在应用一些散列函数(Hn-1散列函数,其中n> 1)时实现了这一点。
问题:当我们寻找(检索)任何元素(如果使用不同的散列函数)时,有人可以解释一下CLR映射如何键入哈希码? CLR如何跟踪(如果是)任何活动对象(哈希表)的哈希函数?
提前致谢。
答案 0 :(得分:1)
哈希码不能唯一标识对象。它只是用来快速将该对象放入桶中。一个存储桶中的元素可能但不必相等,但不同存储桶中的元素必须不相等。
从概念上讲,您可以将引用类型的默认GetHashCode()
实现视为在每个实例中使用一个字段,该字段包含在对象创建时初始化的哈希码的随机值。实际的实现有点复杂,但这并不重要。
由于只有20亿个不同的哈希码,如果你有更多的元素,大多数哈希表实现的O(1)
运行时将会崩溃。当然,分发必须是好的,即一定不能有太多的哈希冲突,但有一些不是大问题。
对于具有值语义的类型,您始终覆盖Equals
和GetHashCode
以使用确定相等的字段。
答案 1 :(得分:1)
从概念上讲,有两个哈希函数。您可能已经猜到,第一个哈希函数是关键对象的GetHashCode
方法。第二个哈希函数是第一个哈希函数返回的密钥的哈希值。
因此,设想一个容量为1,024项的哈希表,并且您将插入两个键:K1
和K2
。
K1.GetHashCode()
返回1,023。 K2.GetHashCode()
返回65,535
然后代码将返回的密钥除以散列表大小并取余数。因此,两个键都映射到哈希表中的位置1,023。
K1
已添加到表格中。当需要添加K2
时,会发生冲突。所以代码转向第二个哈希函数。第二个散列函数可能是某种类型的“位混合器”(通常是计算散列码的最后一个阶段),它使返回密钥中的位随机化。从概念上讲,代码看起来像这样:
int hashCode = K2.GetHashCode();
int slot = hashCode % 1024;
if (table[slot] != null)
{
int secondHashCode = BitMixer(hashCode);
slot = secondHashCode % 1024;
}
这里的要点是代码不必跟踪不同键的多个散列函数。它知道它可以调用Key.GetHashCode()
来获取对象的哈希码。从那里,它可以调用自己的位混合器函数或函数来生成额外的哈希码。
答案 2 :(得分:0)
不确定我是否理解你的问题,但是.NET中的每个对象都实现了GetHashCode
函数,该函数返回在字典/哈希表中可用(和使用)的哈希码,因此对象本身负责生成一个好的哈希码。
当然,由于哈希码是一个int,因此可能(并且将会)存在疑虑。冲突由字典/哈希表处理/解决。
答案 3 :(得分:0)
每个对象都实现GetHashCode()
函数和Equals()
函数。
这些的默认实现与对象引用相关。例如,a.Equals(b)
将返回与object.ReferenceEquals(a,b)
相同的内容。这意味着如果两个对象引用相同,那么它们的哈希代码也是如此。
有些情况下,您需要为Equals()
函数提供不同的语义。在这些情况下,您必须维持合同,a.Equals(b)
然后a.GetHashCode() == b.GetHashCode()
。
使用的哈希函数很多,每个函数都有自己的优点和缺点。有一个有用的解释here。使用的实际功能不是你应该担心的,在Hashtable中保持平均 o(1)查找时间最重要的是(理想情况下)确保将要插入的对象具有它们的GetHashCode()
结果尽可能接近均匀分布。