HashMap 在 JDK 1.8 相比 1.7 有哪些优化,为什么改变插入方式?
未标记
考查重点:桶结构与扩容迁移变化。JDK 8 增加长桶树化,扩容时按旧容量位将节点分成两组并保持组内顺序;结合源码解释它如何区别于 JDK 7 的迁移方式
本题目录
参考答案
核心回答
JDK 8 的 HashMap 相比 JDK 7,主要优化了冲突桶结构、扩容迁移和哈希扰动。以下对比以 OpenJDK 7u80 与 8u412 的实现为例:
- 长冲突链可以转成红黑树。
JDK7 主要使用数组加链表;JDK8 在满足条件时树化,减少大量冲突下长链扫描的代价。表容量较小时会优先扩容,并非一发生碰撞就建树。 - 扩容按新增的一位拆分。 容量翻倍后,节点留在原桶,或移动到“原桶下标加旧容量”的位置,可以依据节点已保存的
hash直接拆成两个目标组,简化重新定位。 - 链表插入与迁移不再依靠头插反转。
JDK8 普通链表追加新键时使用尾插,扩容也保持拆分后各组的相对顺序,避开了JDK7 头插迁移反转链表所关联的经典环链竞争路径。 - 哈希扰动更简洁。
JDK8 用原始hashCode与其高 16 位异或,使高位参与桶定位,配合树化等机制处理碰撞。
这些改变改善了冲突和迁移处理,没有把 HashMap 改成线程安全容器,也没有增加遍历顺序保证。
头插与尾插要分清两处操作
第一处是普通 put:JDK 7 新增冲突节点时可直接放到桶首;JDK 8 在检查链表是否已有相等键的过程中,未找到则追加到尾部。已有键仍是更新其值,并不是每次 put 都追加新节点;树桶也走自己的插入逻辑。
第二处是扩容迁移,也是历史并发环链的关键。JDK 7 遍历旧链,把当前节点头插到新桶,会改变 next 指向并反转同一目标桶内的顺序。多个线程无同步地交错迁移同一批节点,可能利用已被另一个线程改动的链接形成环。
JDK 8 对普通链表维护低位组和高位组的头尾指针,按旧链顺序追加到相应组,再分别接到新表,避免这种反转过程。不过“尾插”不是同步手段:两个线程同时插入仍可能覆盖桶入口、丢失计数或产生其他不一致。不能将“换尾插”解释成所有并发问题已经解决。
扩容为什么只看一位
桶容量是 2 的幂,定位使用“内部 hash 与容量减一按位与”。例如容量从 16 变为 32,掩码从 15 变为 31,只多考虑值为 16 的那一位:
- 该位为 0,节点仍在原下标;
- 该位为 1,节点移到原下标加 16。
例如内部 hash 为 3 和 19,旧表中都在下标 3;扩容后分别在 3 和 19。这里使用的是节点保存的内部 hash,不是说重新调用每个键的 hashCode。
JDK 7 迁移会逐节点按新容量计算目标下标,部分路径还可能因替代哈希机制变化重新计算 hash;不能笼统说 JDK 7 每次扩容都重新调用所有键的 hashCode。JDK 8 的优化在于直接按这个新增位把旧桶拆成两组,扩容仍需遍历和处理旧节点,并非 O(1)。
树化为什么不能只背一个阈值
树节点比普通链表节点更复杂、占用更多空间,所以只在冲突足够多且表容量合适时使用;表太小,扩容可能已经能把碰撞节点分开。树化改善的是冲突桶的处理,良好的 hashCode 和正确的 equals 仍然重要,不能承诺任意键、任意操作都获得同一种复杂度。
“保持拆分后各组的相对顺序”也不等于保持整张 Map 的遍历顺序。HashMap 的迭代顺序始终没有契约保证,需要顺序时应选择满足相应顺序语义的容器。
参考资料
- 国内:JavaGuide:HashMap 源码分析:参考 JDK 7/8 桶结构、哈希扰动、插入和扩容的比较,组织正文中的版本变化及树化与扩容的配合条件。
- 国内:美团技术团队:Java 8 系列之重新认识 HashMap:用于比较头插迁移和 JDK 8 的高低位拆分,解释扩容后节点位置及顺序变化。
- 国内:沉默王二:Java HashMap 详解:用于说明内部 hash、桶下标和容量翻倍的关系,帮助区分重新定位与重新计算键的哈希。
- 国外:OpenJDK:JEP 180,Handle Frequent HashMap Collisions with Balanced Trees:用于说明 JDK 8 引入树化的目标、管理树结构的代价及迭代顺序仍受原有契约约束。
- 国外:Oracle:HashMap,Java SE 8:用于核对 JDK 8 仍需外部同步、遍历顺序不受保证,以及 fail-fast 不能作为并发安全机制。
相关面经
7 篇公司未注明
后端开发工程师Java中HashMap的底层数据结构是什么?还有它的扩容机制是什么样的?在哪个JDK版本有改变的?
蚂蚁集团
说下 HashMap 的原理,JDK1.8 里 HashMap 做了什么优化?这些优化解决了之前的什么问题?另外它的并发安全问题怎么解?
小红书
AI Native 全栈开发工程师HashMap 1.7 / 1.8 区别
法大大
Java 开发工程师JDK8对HashMap有什么优化吗
阿里巴巴
后端开发工程师(AI)HashMap的底层实现讲一下?1.7和1.8有什 么区别?


