Java 为什么HashMap会重新显示key对象提供的hashcode?

Java 为什么HashMap会重新显示key对象提供的hashcode?,java,collections,hashmap,hash,hashcode,Java,Collections,Hashmap,Hash,Hashcode,我正在阅读Java 1.6 API提供的HashMap类的代码,无法完全理解以下操作的需要(在put和get方法主体中找到): 其中方法hash()具有以下主体: private static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } 这可以通过对提供的哈希代码执行位操作

我正在阅读Java 1.6 API提供的HashMap类的代码,无法完全理解以下操作的需要(在put和get方法主体中找到):

其中方法
hash()
具有以下主体:

 private static int hash(int h) {
         h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
这可以通过对提供的哈希代码执行位操作来有效地重新计算哈希。我无法理解这样做的必要性,即使API声明如下:

        h1 = h1 ^ h13 ^ h21 ^ h9 ^ h6     
        h2 = h2 ^ h14 ^ h22 ^ h10 ^ h7
        h3 = h3 ^ h15 ^ h23 ^ h11 ^ h8
        h4 = h4 ^ h16 ^ h24 ^ h12 ^ h9
        h5 = h5 ^ h17 ^ h25 ^ h13 ^ h10
这是至关重要的 因为HashMap使用两个长度哈希表的幂,所以 否则,会遇到哈希代码的冲突,而哈希代码没有差异 在低位

我知道键值PAR存储在一个数据结构数组中,数组中某个项的索引位置由其散列决定。
我不明白的是,这个函数如何向散列分布中添加任何值。

我在某个地方读到,这样做是为了确保良好的分布,即使您的散列代码实现很糟糕。

正如Helper所写,它只是为了防止现有的密钥对象哈希函数出现故障,并且不能很好地混合低位。据pgras引用,

 /**
  * Returns index for hash code h.
  */
 static int indexFor(int h, int length) {
     return h & (length-1);
 }
散列以两个长度的幂进行AND运算(因此,
length-1
保证为1的序列)。由于此ANDing,仅使用
h
的低位。其余的
h
被忽略。想象一下,不管出于什么原因,原始散列只返回可被2整除的数字。如果直接使用它,hashmap的奇数位置将永远不会被使用,从而导致冲突数量增加x2。在真正病态的情况下,一个坏的哈希函数会使哈希映射的行为更像一个列表,而不是一个O(1)容器


Sun工程师必须运行测试,表明太多的哈希函数在其低位中不够随机,并且许多哈希映射不够大,无法使用高位。在这些情况下,HashMap的
散列(int h)
中的位操作可以提供比大多数预期用例更好的性能(由于较低的冲突率),即使需要额外的计算。

如您所知,HashMap的底层实现是一个散列表,特别是一个封闭的bucket散列表。负载系数确定集合中对象的适当数量/桶的总数

假设您不断添加更多元素。每次这样做时,它都会运行对象的hashcode方法,并使用带模运算符的bucket数来决定对象应该放入哪个bucket

随着n(集合中元素的数量)/m(存储桶的数量)越来越大,您的读写性能越来越差

假设您的hashcode算法是惊人的,那么性能仍然取决于这个比较n/m

重新灰化还用于更改存储桶的数量,并保持与构建集合时相同的负载系数


请记住,任何哈希实现的主要好处都是读写的理想O(1)性能。

如您所知,object.hashCode()可以被用户覆盖,因此非常糟糕的实现会抛出非随机的低级位。这会使一些桶挤得满满的,并且会留下许多桶没有装满

我刚刚创建了一个可视化的地图,显示了他们在散列中试图做什么。看起来散列(inth)方法只是通过位级的运算来创建一个随机数,这样得到的数字就更随机(从而更均匀地进入存储桶)

每个位重新映射到不同的位,如下所示:

        h1 = h1 ^ h13 ^ h21 ^ h9 ^ h6     
        h2 = h2 ^ h14 ^ h22 ^ h10 ^ h7
        h3 = h3 ^ h15 ^ h23 ^ h11 ^ h8
        h4 = h4 ^ h16 ^ h24 ^ h12 ^ h9
        h5 = h5 ^ h17 ^ h25 ^ h13 ^ h10

直到h12


正如你所看到的,h的每一位都离它自己很远。所以它将是非常随机的,不会挤满任何特定的桶。希望这有帮助。如果需要完全可视化,请向我发送电子邮件。

对,java.lang.Object中的默认hashcode()实现在散列之间没有太多分布。我不明白的是,如果每个散列都是唯一的(并且所讨论的方法没有也不能解决唯一散列的问题),那么该机制会面临什么问题?它提到了一些关于低阶位冲突的东西-但这不是很清楚。根据定义,每个哈希都不是唯一的。。。我不能很好地回答您的问题,但问题在于返回“hashCode&(length-1)”…“以防万一”的“indexFor”方法?事实上,Java中的大多数哈希代码都将是垃圾代码。例如,看看java.lang.Integer!但这实际上是有道理的。最好说“如果每个人的Object.hashCode()都有糟糕的位分布,这没关系,只要他们遵循equal Object have equal hashcodes规则,并尽可能避免冲突。”然后只有像HashMap这样的集合实现才有通过辅助哈希函数传递这些值的负担,这不是每个人的问题。“hashmap的奇数位置永远不会被使用”我不明白。你能举个例子吗?好的,假设我对Employee对象进行散列,我的所有员工都有一个int-ID字段,如“400114”、“400214”、“400314”等等(他们都共享“14”他们身份证的一部分,因为那是我所在部门的后缀)。Integer的hashCode()方法返回整数本身——因此,如果我在HashSet/without/HashMap的hash(inth)中使用雇员ID作为键,那么分布将非常非常不均匀。在这个例子中,因为14是偶数,所以只能使用偶数桶。@tucuxi所以我可以把
hash(int h)
看作偶数分布的二次散列吗??