高效计算索引(核心原因)
- HashMap通过哈希值确定元素在数组中的位置,计算索引的公式为:
index = hash(key) & (n - 1)
其中n
是数组长度,&
是按位与运算。 - 当
n
是2的幂时 ,n-1
的二进制形式为全1(例如16-1=15
→1111
)。
此时hash & (n-1)
等效于hash % n
(取模运算),但位运算比取模快数十倍。 - 若
n
不是2的幂 ,n-1
的二进制中会出现0(例如n=15
时,n-1=14
→1110
)。
这将导致某些索引永远无法被访问(例如末位为1的位置),浪费空间且增加哈希冲突。
2. 优化哈希分布
- 当
n-1
为全1时,哈希值的所有低位都能参与索引计算 。
例如n=16
时,哈希值的低4位决定索引(1111
覆盖4位),分布更均匀。 - 若
n-1
非全1(如n=10
时n-1=1001
),部分比特位被忽略,导致哈希值的高位变化无法影响索引,加剧冲突。
3. 扩容时的性能优化
-
HashMap扩容时,新容量 = 旧容量 × 2(保持2的幂)。
-
元素在新数组中的位置只需判断新增的最高比特位:
- 若最高位为0 → 索引不变(
原位置
)。 - 若最高位为1 → 索引 =
原位置 + 旧容量
。
- 若最高位为0 → 索引不变(
-
无需重新计算哈希 ,直接通过位运算移动数据(如JDK源码中的
e.hash & oldCap
判断),性能极高。
4. 避免取模运算的开销
- 取模运算(
%
)涉及除法,CPU执行成本高。
通过hash & (n-1)
替代hash % n
,消除了除法指令,提升计算效率。
此方法通过位操作,将任意整数向上取整为最小的2的幂(如输入10,输出16)。
总结
原因 | 效果 |
---|---|
位运算替代取模 | 索引计算速度大幅提升(CPU指令优化) |
哈希分布更均匀 | 减少冲突,提升查询效率 |
扩容迁移数据高效 | 无需重新哈希,直接位判断新位置 |
避免空间浪费 | 所有索引位均可被访问 |
这种设计在时间(计算速度)和空间(分布均匀性)上达到了平衡,是HashMap高性能的关键之一