一、HashMap核心实现原理
1. 底层数据结构
HashMap采用数组+链表+红黑树的复合结构:
-
数组(桶数组):HashMap的主干,每个数组元素称为一个"桶"(bucket)
-
链表:解决哈希冲突的链式存储结构(JDK7及以前只有链表)
-
红黑树:JDK8新增,当链表过长时转换为红黑树以提高性能
2. 哈希计算过程
static final int hash(Object key) {
int h;
// 1. 获取key的hashCode
// 2. 高16位与低16位异或(扰动函数,减少哈希冲突)
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// 确定桶位置的方法
index = (n - 1) & hash // n是桶数组长度
3. 键值对存储
HashMap内部使用Node<K,V>类存储键值对:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; // 哈希值
final K key; // 键
V value; // 值
Node<K,V> next; // 下一个节点(链表或树节点)
}
二、扩容机制深度分析
1. 扩容触发条件
当满足以下任一条件时触发扩容:
-
元素数量 > 容量 × 负载因子(默认容量16,负载因子0.75,即12个元素时扩容)
-
链表长度 > 8但桶数组长度 < 64(优先扩容而不是树化)
2. 扩容过程(resize())
-
计算新容量:原容量×2(保证始终是2的幂)
-
创建新数组:新建一个2倍大小的桶数组
-
数据迁移(重要优化点):
-
JDK8优化:节点在新数组中的位置要么是原位置,要么是原位置+原容量
-
不需要重新计算hash,通过
(e.hash & oldCap) == 0判断位置
-
-
树形节点拆分:如果迁移时发现是树节点,会将树拆分为两个链表,必要时退化为链表
3. 扩容性能影响
-
时间复杂度:O(n),需要重新计算所有元素的位置
-
优化措施:使用Iterator的批量迁移、高位低位链表分离
三、树化(链表转红黑树)机制
1. 树化触发条件
同时满足以下两个条件时链表转为红黑树:
-
链表长度 ≥ TREEIFY_THRESHOLD(默认8)
-
桶数组长度 ≥ MIN_TREEIFY_CAPACITY(默认64)
2. 树化实现细节
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
// 先检查数组长度是否达到最小树化容量
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize(); // 优先扩容
else if ((e = tab[index = (n - 1) & hash]) != null) {
// 树化转换过程...
}
}
3. 树化阈值设计原理
-
为什么选择8作为阈值:
-
基于泊松分布统计,链表长度达到8的概率极低(约0.00000006)
-
红黑树的查找时间复杂度为O(log n),链表为O(n),n较小时链表性能更好
-
树节点占用空间是普通节点的两倍,权衡空间和时间成本
-
-
退化条件:当树节点数 ≤ UNTREEIFY_THRESHOLD(默认6)时退化为链表
-
设置6(而不是8)避免频繁树化和退化
-
四、线程安全问题分析
1. 典型并发问题
-
死循环:JDK7扩容时链表倒置可能导致环形链表(JDK8已修复)
-
数据丢失:并发put可能导致元素覆盖
-
size不准确:并发修改导致size计算错误
2. 解决方案
-
使用Collections.synchronizedMap
-
使用ConcurrentHashMap
-
使用外部锁同步
五、性能优化建议
-
初始化时设置合理容量:
// 预估元素数量100,负载因子0.75 new HashMap<>(128); // 100/0.75 = 133.33 → 向上取2的幂:128 -
键对象设计:
-
实现良好的
hashCode()方法(分布均匀) -
不可变对象作为键更安全
-
-
监控指标:
-
桶利用率(非空桶比例)
-
平均链表/树长度
-
最大链表/树长度
-
六、JDK不同版本的演进
-
JDK7与JDK8的主要区别:
特性 JDK7 JDK8 冲突解决 纯链表 链表+红黑树 哈希计算 多次扰动 一次扰动 扩容方式 头插法(可能死循环) 尾插法+高低位拆分 节点结构 Entry Node/TreeNode -
后续版本优化:
-
JDK9:更紧凑的存储结构
-
JDK16:增加可观测性API
-
理解HashMap的实现原理不仅有助于面试,更能帮助开发者在实际应用中做出合理的设计选择,避免性能陷阱。

1万+

被折叠的 条评论
为什么被折叠?



