外观
一句话答案
JDK8 的 HashMap 是数组+链表+红黑树,默认容量 16 负载因子 0.75,链表长度 ≥8 且数组 ≥64 时转红黑树。
核心要点
JDK 8 的 HashMap 结构:数组 + 链表 + 红黑树
数组(Node[] table)
index 0: null
index 1: Node(key1, val1) → Node(key2, val2) → null ← 链表(哈希冲突)
index 2: TreeNode(...) ← 红黑树(链表长度≥8后转换)
index 3: null
...
index n: Node(keyN, valN)核心字段:
java
transient Node<K,V>[] table; // 哈希桶数组
transient int size; // 实际键值对数量
int threshold; // 扩容阈值 = capacity * loadFactor
final float loadFactor; // 负载因子,默认 0.75
transient int modCount; // 修改计数,fail-fast 用哈希计算(扰动函数):
java
static final int hash(Object key) {
int h;
// 高16位与低16位异或,目的:让高位参与散列,减少冲突
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// 确定数组下标:(capacity - 1) & hash
// 等价于 hash % capacity(因为 capacity 是2的幂)为什么 capacity 必须是 2 的幂? 使得 hash % capacity 可以用位运算 (capacity-1) & hash 代替,效率更高,且 capacity-1 全为 1,使 hash 低位都能参与索引计算,分布更均匀。
追问与易错
追问方向:
- 为什么链表转红黑树的阈值是 8?(泊松分布,链表长度达到 8 的概率极低)
- HashMap 是线程安全的吗?并发下会出什么问题?(数据覆盖/死循环 JDK7)
- 为什么容量必须是 2 的幂次?(hash & (n-1) 等价于取模但更快)
易错点:
- ❌ "红黑树在链表长度到 8 就转换"——还需数组长度 ≥ 64,否则优先扩容
- ❌ 混淆 hashCode 和 equals 的关系——equals 相��则 hashCode 必须相等,反之不一定
💡 记忆锚点
16层公寓按门牌取模入住,冲突排队(链表),队伍超8人且楼层超64层时换红黑树调度,住满75%整栋翻倍扩建。hash先高低16位异或"搅匀"再定位。