HashMap 的容量为什么常用 2 的幂?

0 阅读1分钟

因为 2 的幂让 HashMap 能用最低成本的位运算完成分桶,并且让扩容时的旧节点搬迁保持简单。

JDK 17 的 putVal 计算下标时使用:

i = (n - 1) & hash;

容量 n 是 2 的幂时,n - 1 的低位全是 1。这个位与运算保留 hash 的低几位,效果相当于非负 hash 对 n 取模,但不用做除法,而且结果一定落在 [0, n - 1]。Java 的 hashCode() 可以是负数,直接取模可能得到负下标,位与没有这个问题。

扩容到两倍时,旧容量 oldCap 也是 2 的幂。节点只需要判断:

(hash & oldCap) == 0

这位要么是 0,要么是 1。结果是 0,节点留在原下标;结果不是 0,节点移动到“原下标 + oldCap”。这样不必重新计算每个节点的完整取模,也能把旧桶拆成两组。

只用低位会丢掉高位信息,所以 HashMap 还用 h ^ (h >>> 16) 做一次高位扰动。它不是为了制造一个密码学哈希,而是尽力让高位也影响低位分布。

这只是当前 HashMap 的实现取舍。容量是 2 的幂不等于不会碰撞,也不等于所有哈希表都必须这样设计。