hashmap数组长度必须是2的幂次方,因为(n-1)&hash仅在此条件下等价于hash%n,既提升下标计算速度(位运算快于取模),又保证哈希分布均匀、扩容高效(翻倍扩容时仅需判断新增高位,免重哈希)。

因为底层用 (n - 1) & hash 快速算下标,这个公式只有在数组长度 n 是 2 的幂次方时,才等价于 hash % n,同时还能保证分布均匀、扩容高效。
下标计算靠位运算,不是取模
HashMap 不用 hash % n,而用 (n - 1) & hash 算数组索引。原因很简单:位与(&)比取模(%)快得多,尤其在高频 put/get 场景下优势明显。
但这个优化有个硬前提:
- n = 16 → n−1 = 15 = 01111₂,和任意 hash 做 &,结果只保留低 4 位,范围正好是 0~15
- n = 32 → n−1 = 31 = 011111₂,保留低 5 位,范围 0~31
- 只要 n 是 2 的幂,n−1 的二进制就全是 1,能完整参与 hash 低位运算
非 2 的幂会导致桶严重失衡
比如手动传入容量 10:
- n = 10 → n−1 = 9 = 1001₂,中间两位恒为 0
- hash 的第 1、2 位(从 0 开始数)无论 0 或 1,都不影响结果
- 大量不同 hash 被压到相同几个下标(如 0、1、8、9),其余桶长期空闲
- 链表变长,查询退化成 O(n),冲突率飙升
扩容时能免重哈希,只看新增高位
容量从 16 扩到 32,新掩码多出一位(第 4 位):
- 若 hash 在该位为 0 → 新下标 = 原下标
- 若 hash 在该位为 1 → 新下标 = 原下标 + 16
- 不需要重新调用 hashCode(),也不用再算一遍 & 或 %
- 这套逻辑依赖“翻倍即升一位”,只有 2 的幂才能自然支持
你设啥值它都自动对齐到 2 的幂
就算写 new HashMap(13) 或 new HashMap(100):
- 内部会调用
tableSizeFor(13)→ 得到 16 -
tableSizeFor(100)→ 得到 128 - 核心逻辑是:把输入最高位设 1,后面全填 1,再加 1
- 这是强制保障,不是可选项——所有实例都走同一套高效路径
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











