HashMap 如何计算 hash 值?
HashMap 如何计算 hash 值?
这段代码是 HashMap 中用于计算哈希值的 hash() 方法。它的主要作用是对键(key)的原始哈希码进行二次处理,以减少哈希冲突的概率。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
- 输入:任意对象
key(允许为null)。 - 输出:计算后的哈希值(
int类型)。 - 逻辑:
- 如果
key为null,直接返回0(这是为了支持null键)。 - 否则:
- 调用
key.hashCode()获取原始哈希值h。 - 将
h无符号右移 16 位(即h >>> 16),得到高 16 位的值。 - 将原始哈希值
h与右移后的值进行 异或(XOR) 操作,得到最终哈希值。
- 调用
- 如果
为什么需要这样设计?
1. 问题背景
- 当哈希表较小时(如容量为 16),键的哈希值通过取模运算(
hash & (tableSize - 1))确定存储位置。若键的哈希值仅在高位不同(例如Float键的哈希码),而低位相同,会导致这些键被映射到同一位置,引发冲突。 - 例如:
Float键1.0和2.0的哈希码分别为0x3F800000和0x40000000。在表大小为 16 时,计算索引为hash & 15,两者的低位均为0,导致冲突。
2. 解决方案
- 通过
h ^ (h >>> 16),将哈希值的高 16 位信息“混合”到低 16 位中。这样在计算索引时(hash & (tableSize - 1)),高位的信息也能参与运算,从而减少冲突。
3. 为什么用 XOR?
- XOR 操作能均匀分布比特变化(比加法或乘法更简单且高效)。
- 右移 16 位是为了让高位信息影响低位,同时保留部分原始哈希值的特性。
例子
假设 key.hashCode() 为 0x12345678,tableSize = 16:
- 原始哈希值:
00010010 00110100 01010110 01111000(0x12345678)。 - 右移 16 位:
00000000 00000000 00010010 00110100(0x00001234)。 - 异或结果:
00010010 00110100 01010110 01111000(h)
^
00000000 00000000 00010010 00110100(h >>> 16)
=
00010010 00110100 01000100 01001100(0x1234444C)。 - 计算索引:
0x1234444C & 15 = 0xC & 0xF = 12。
如果没有混合高位,原始哈希的索引是 0x78 & 15 = 8,而混合后变为 12,避免了某些冲突。
总结
这段代码通过简单的位运算(XOR + 右移)将哈希值的高位信息扩散到低位,从而在哈希表大小是 2 的幂时减少冲突概率,同时保持高效性。

浙公网安备 33010602011771号