jdk 1.8 hashmap 底层采用 node 数组+链表+红黑树,尾插法插入、优化哈希计算、扩容时无需重算 hash 且支持懒初始化;jdk 1.7 为 entry 数组+单向链表、头插法、原始 hashcode 直接取模、扩容开销大。

Java 中 HashMap 的底层原理在 JDK 1.7 和 1.8 之间有实质性演进,核心差异集中在数据结构、插入方式、扩容逻辑和冲突处理策略上。面试或调优时,重点掌握 JDK 1.8 的设计即可,但理解 1.7 的局限性能帮你更深入把握改进动机。
数据结构与节点类型不同
JDK 1.7 使用 Entry 数组 + 单向链表,每个 Entry 包含 key、value、hash 和 next 字段;JDK 1.8 改为 Node 数组 + 链表 + 红黑树,引入 TreeNode 类型支持树化。当某个桶(数组索引)中链表长度 ≥ 8 且数组总长度 ≥ 64 时,链表自动转为红黑树,查询时间从 O(n) 降至 O(log n);若树中节点数 ≤ 6,则退化回链表,兼顾空间与效率。
插入方式从头插法改为尾插法
JDK 1.7 在链表中采用头插法:新节点始终放在链表头部,next 指向原头节点。这在多线程扩容时可能引发环形链表,导致 get() 死循环;JDK 1.8 改为尾插法:新节点追加到链表末尾,保持插入顺序,彻底规避该风险。同时,Node 节点的 hash 字段被 final 修饰,确保哈希值不可变。
哈希计算与索引定位更高效
两者都用 (n - 1) & hash 计算数组下标(n 为 2 的幂),但 JDK 1.8 的 hash 函数做了优化:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
即对 hashCode 高低 16 位异或,增强低位散列性,减少因低位相同导致的哈希碰撞。JDK 1.7 则直接使用原始 hashCode,分布均匀性较弱。
扩容机制大幅优化
JDK 1.7 扩容需对所有元素重新计算 hash 并取模,开销大;JDK 1.8 利用数组长度为 2 的幂这一特性,通过判断 (e.hash & oldCap) == 0 快速决定节点留在原位置还是移至 原索引 + 旧容量,无需重算 hash,且保持链表/树中节点相对顺序。另外,JDK 1.8 是懒初始化:构造 HashMap 时不创建 table 数组,首次 put 才调用 resize() 初始化,默认容量仍为 16,阈值为 12。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











