HashMap允许一个null键,因其hash()方法对null返回固定值0并存入table[0];非null键则通过(h = key.hashCode()) ^ (h >>> 16)扰动哈希,再用(n-1) & hash定位下标,兼顾均匀性与性能。

Java HashMap 计算键的哈希值,核心是调用 hash(Object key) 方法,它对原始 hashCode() 做了扰动处理,不是直接使用对象的哈希码。
key 为 null 时的处理
如果键是 null,HashMap 直接返回哈希值 0,并把该键值对固定存放在数组索引为 0 的位置(即第一个桶)。
非 null 键的哈希扰动公式
对于非空键,计算方式为:
(h = key.hashCode()) ^ (h >>> 16)
立即学习“Java免费学习笔记(深入)”;
这个公式分三步:
- 先调用
key.hashCode()得到原始哈希码h - 将
h无符号右移 16 位(h >>> 16),取出高 16 位 - 再与原哈希码
h进行异或(^),让高 16 位参与低位运算
这样做的目的是增强低位的随机性。因为后续用 (n - 1) & hash 算下标时,只依赖哈希值的低几位(比如容量 16 时,只看低 4 位),若原始 hashCode 高位变化大、低位重复多,就容易导致大量元素挤在少数桶里。扰动后,高位信息“混入”低位,分布更均匀,减少哈希碰撞。
哈希值如何决定存储位置
得到扰动后的哈希值后,HashMap 用以下方式定位数组下标:
index = (n - 1) & hash
其中 n 是数组长度(必须是 2 的幂,如默认 16)。这个位运算等价于取模 hash % n,但效率更高。由于 n 是 2 的幂,n - 1 的二进制全是 1(如 15 是 1111),所以 & 操作能精准保留 hash 的低 log₂(n) 位,作为有效索引。
为什么不用 hash % n
取模运算涉及除法,在 CPU 上比位运算是慢操作。而 (n - 1) & hash 只需一次位与,性能优势明显。前提是数组长度必须是 2 的幂——这也是 HashMap 所有扩容都翻倍(16 → 32 → 64…)的根本原因。


















