(n - 1) & hash 是 hashmap 桶位计算公式,因数组长度 n 恒为 2 的幂,故 (n - 1) 二进制全为 1,该位运算等价于 hash % n 且效率更高;扰动函数 h ^ (h >>> 16) 将高位异或入低位,避免低位重复导致哈希碰撞。

Java HashMap 的桶位计算公式是 (n - 1) & hash,其中 n 是数组(table)长度,hash 是经过扰动处理后的键哈希值。它不是数学推导出来的理论公式,而是基于两个关键前提的工程优化选择:数组长度恒为 2 的幂次方、位运算比取模快。
为什么用 (n - 1) & hash 而不用 hash % n
因为 HashMap 要求 table.length 始终是 2 的整数次幂(如 16、32、64…),此时 n 的二进制形式是 1 后面跟着若干个 0(例如 16 → 10000),那么 n - 1 就是全 1 的低位掩码(16−1=15 → 01111)。对任意整数 hash 执行 (n − 1) & hash,等价于只保留 hash 的低 log₂(n) 位,其余高位被清零——这恰好就是 hash % n 的结果。
例如:
n = 16 → n−1 = 15 → 二进制 00001111
hash = 137 → 二进制 10001001
137 & 15 = 10001001 & 00001111 = 00001001 = 9
而 137 % 16 = 9,结果一致。
扰动函数 hash() 的作用是让高位参与索引计算
原始 hashCode() 可能分布不均,尤其当 key 是连续整数或字符串前缀相同时,低几位往往高度重复,导致大量元素挤在少数几个桶里。HashMap 的扰动函数:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
把高 16 位和低 16 位异或,使高位信息“混合”进低 16 位。这样即使原 hash 低 bits 相同,扰动后也更可能不同,大幅降低碰撞概率。
扩容时桶位置重分配依赖这个公式结构
扩容时新容量为 oldCap × 2,新数组长度 newCap = oldCap
- 保持原索引 i
- 落在 i + oldCap
判断依据正是 (e.hash & oldCap) == 0:因为 oldCap 是 2 的幂,其二进制只有一位为 1(比如 16 → 10000),该位正好对应新旧索引差异的“分界位”。这个快速判断能避免重新计算全部 hash,是 resize 高效的关键。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











