为什么hashmap的初始容量及扩容倍数必须是2的幂

因为HashMap底层依赖位运算快速定位桶位置,而只有容量是2的幂时,(n – 1) & hash 才能等价于 hash % n,同时保证索引均匀、冲突可控、扩容高效。

加快索引计算速度

HashMap不使用取模(%)而是用位与(&)算数组下标,关键公式是:(table.length – 1) & hash。当长度 n 是 2 的幂(如 16、32、64),n−1 的二进制全是 1(如 15 → 1111),此时与 hash 做按位与,相当于只保留 hash 的低几位——这和取模效果一致,但位运算快得多。

例如:hash = 103,n = 16 → n−1 = 15(1111₂),103(1100111₂)& 15 = 7 → 定位到索引 7若 n = 10(非 2 的幂),n−1 = 9(1001₂),103 & 9 = 1,低位“0”会屏蔽 hash 的部分信息,导致大量 key 挤在少数桶里

避免索引分布失衡

如果容量不是 2 的幂,n−1 的二进制含 0,按位与会固定丢弃某些位,使 hash 值的分布能力大打折扣。

n = 10 → n−1 = 9(1001₂),第二、三位恒为 0 → hash 的第 2、3 位无论 0 或 1 都不影响结果结果就是:大量不同 hash 值映射到相同索引,链表/红黑树变长,查询退化为 O(n)而 n = 16(10000₂)→ n−1 = 15(01111₂),低 4 位全参与运算,hash 变化能充分反映到索引上

让扩容过程免重哈希

扩容时容量翻倍(如 16 → 32),旧桶中每个元素只需看 hash 在新增最高位是否为 1,就能决定留在原位还是移到 原索引 + 旧容量 的位置。

旧容量 16 → 掩码 15(01111),新容量 32 → 掩码 31(11111)新增的第 5 位(从 0 开始数)决定了迁移方向:为 0 → 索引不变;为 1 → 索引 + 16不需要重新调用 hashCode() 或完整再算一遍 hash % 32,大幅提升扩容吞吐量

初始容量自动对齐 2 的幂

即使你传入 new HashMap(10),HashMap 内部也会通过 tableSizeFor(10) 调整为 16;传入 100 → 得到 128。这是强制保障,不是建议。

核心逻辑是:把输入数字最高位设为 1,其余低位全填 1,再加 1 → 得到最近的更大 2 的幂这样设计抹平了用户误配容量的风险,所有实例都天然适配上述三项优化机制

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。