static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
获取 key 的 hashCode 值,然后将 hashCode 的高 16 位和低 16 位进行异或运算(如果两个数字的对应的二进制位相同,则异或结果的该位为 0;反之为1),这种将高位特性和低位特性结合起来,降低hash冲突概率的方式成为扰动计算;
扰动计算作用:
让 hash 值的分布更加均匀。如果直接使用 key.hashCode(),很可能高位和低位的值都很相近,导致 hash 值分布不均,最终存储位置的分布也不均匀,容易发生冲突
举个例子:
假设我们有这样的输入:1, 2, 3, 4, 5, 6, 7
如果不做扰动计算,直接使用 key.hashCode(),那么 hash 值可能是:
1 -> 1
2 -> 2
3 -> 3
4 -> 4
5 -> 5
6 -> 6
7 -> 7
可以看到,hash 值是连续的 1 到 7,那么它们很可能也连续地散列到表中的位置,例如
索引: 0 1 2 3 4 5 6
值: 1 2 3 4 5 6 7
这会使得 1 和 2,3 和 4 等都映射到相邻的位置,极易发生冲突,hash 分布很不均匀。现在使用 HashMap 的扰动函数进行计算:1 -> 1 ^ (1 >>> 16) = 1 ^ 1 = 0
2 -> 2 ^ (2 >>> 16) = 2 ^ 0 = 2
3 -> 3 ^ (3 >>> 16) = 3 ^ 0 = 3
4 -> 4 ^ (4 >>> 16) = 4 ^ 0 = 4
5 -> 5 ^ (5 >>> 16) = 5 ^ 0 = 5
6 -> 6 ^ (6 >>> 16) = 6 ^ 0 = 6
7 -> 7 ^ (7 >>> 16) = 7 ^ 0 = 7 可以看到,通过异或运算,1 的 hash 值变成了 0,和其他 hash 值的差距变大了。这时,它们散列到表中的位置会更加分散:索引: 0 3 6 2 5 1 4
值: 0 3 6 2 5 1 4 1 被映射到索引 0,和其他位置的差距变大。所以,通过扰动函数,原本连续的 hash 值 1 到 7,变成了 0, 2, 3, 5, 6这样更加分散的序列。这使得它们可以更加均匀地散列到表中的各个位置,分布也更加均匀