(n - 1) & hash替代hash % n的核心原因是位运算比取模快得多,且仅当n为2的幂时,n-1二进制全为1,使该位运算等价于取模并保证索引均匀分布,配合扰动函数h^(h>>>16)可提升低位差异性、降低哈希冲突。

哈希桶定位用 (n - 1) & hash 而不是 hash % n,核心就一点:位运算比取模快得多。计算机执行 & 操作是硬件级的“开关灯”动作,而 % 要走除法指令,耗时高几倍甚至更多。
为什么必须要求 n 是 2 的幂次方?
只有当数组长度 n 是 2 的整数次幂(比如 16、32、64),n - 1 的二进制才全是 1。例如:
- n = 16 → n - 1 = 15 → 二进制是 00001111
- n = 64 → n - 1 = 63 → 二进制是 00111111
这样,hash & (n - 1) 就等价于只保留 hash 的低 log₂(n) 位,高位全被“清零”,结果自然落在 0 到 n-1 范围内——和取模效果完全一致,但全程没做一次除法。
直接取模为啥不行?
假设 n = 17(非 2 的幂),n - 1 = 16(10000₂),那 hash & 16 只能返回 0 或 16,根本覆盖不了 0~16 全部索引。取模运算本身不依赖 n 的形式,但无法用位运算等价替代。HashMap 强制 n 为 2 的幂,就是为了把定位逻辑牢牢锁在高效路径上。
扰动函数 hash() 和这个公式怎么配合?
原始 hashCode 可能低位重复率高(比如对象内存地址相近),直接 & (n - 1) 容易集中打在少数桶里。所以先执行扰动:h ^ (h >>> 16),把高 16 位“混入”低 16 位。这样一来,哪怕两个 key 原始 hash 仅高位不同,扰动后低位也会大概率不同,再经 & 运算就能更均匀地分散到各个桶中。
实际效果有多明显?
在 JDK 源码高频调用路径中(如 put、get),每次定位都要算一次索引。百万次操作下来,省掉百万次除法,CPU 时间节省可观。这不是微优化,而是 HashMap 能稳定维持平均 O(1) 性能的关键基建之一。










