核心目的是让高位信息参与索引计算以提升散列均匀性、降低哈希冲突概率;右移16位因int为32位,可使高16位与低16位充分错位混合,异或则能对称保留高低位特征而不归零或归一。

Java HashMap 的 hash 方法对 key 的原始 hashCode() 做 h ^ (h >>> 16) 运算,核心目的是:让高位信息参与索引计算,从而提升散列均匀性、降低哈希冲突概率。
为什么必须右移 16 位?
int 是 32 位整数,高 16 位和低 16 位各自携带不同特征。但 HashMap 实际定位桶位置时用的是 (n - 1) & hash(n 是数组长度,必为 2 的幂)。由于 n - 1 的二进制形如 000...111(低位全 1,高位全 0),与运算会天然屏蔽掉 hash 的高位——也就是说,若不处理,只有低 16 位(甚至更低)真正影响下标。
- 右移 16 位,相当于把高 16 位“搬”到低 16 位的位置上
- 这样后续异或时,高低位信息才能在低位区域发生混合
- 即使数组很小(如默认 16,
n-1 = 15 = 0b1111),也能让原本被丢弃的高位特征间接影响最终下标
为什么用异或(^)而不是 & 或 |?
异或运算是对称、可逆、且能保留差异性的位操作:
-
&运算倾向“归零”:只要有一位是 0,结果就为 0,容易丢失变化 -
|运算倾向“归一”:只要有一位是 1,结果就为 1,同样削弱区分度 -
^运算“不同为 1,相同为 0”,既不压制也不放大某一位,能最大程度保留原值中 0/1 的分布特征
例如:h = 0xABCDEF01,h >>> 16 = 0x0000ABCD,异或后得到的新 hash 低 16 位是两者的混合,而非简单覆盖或截断。
这个设计如何配合数组长度为 2 的幂?
HashMap 要求容量为 2 的幂,本质是为了用 & 替代取模 % 提升性能。但这也带来副作用:低位掩码导致高位失效。右移异或正是对这一副作用的主动补偿:
- 若不做处理,大量 hashCode 仅低位不同(比如连续 Integer、小范围字符串),会导致严重聚集在少数桶中
- 经过
h ^ (h >>> 16)后,哪怕原始 hash 只有低 8 位变化,高 16 位也会反向扰动低 16 位,使输出更“随机” - 实测表明,该操作可显著改善 String、Integer 等常见 key 类型在小容量下的分布质量
它不是为了加密或绝对唯一,而是用极低成本(两次位操作)换取更鲁棒的平均性能。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











