跳转至

用过哪些 Map 类?有什么区别?HashMap 并发下用什么?

一、常见 Map 实现

Map 线程安全 有序 特点
HashMap 无序 最常用
LinkedHashMap 插入顺序 / 访问顺序 继承 HashMap
TreeMap 按 key 排序 红黑树
Hashtable ✅(全表锁) 无序 遗留类
ConcurrentHashMap 无序 高并发
ConcurrentSkipListMap 按 key 排序 并发版 TreeMap
EnumMap 按枚举顺序 key 是枚举
WeakHashMap 无序 弱引用 key

二、HashMap 内部原理

存储结构

JDK8:数组 + 链表 + 红黑树。

table[0] -> Node -> Node
table[1] -> TreeNode(链表长度 ≥ 8 且数组长度 ≥ 64)

默认容量

16,必须是 2 的幂。

hash 计算

(h = key.hashCode()) ^ (h >>> 16)

扰动函数,让高位也参与下标计算。

扩容

size > capacity * loadFactor(0.75) 时扩容为 2 倍。

树化

链表长度 ≥ 8 且数组长度 ≥ 64 转红黑树;≤ 6 退化为链表。

详见 HashMap 源码问题

三、HashMap 线程安全吗

不安全。多线程下可能: - 扩容成环(JDK7 头插法)。 - 数据覆盖。 - size 不准。

四、并发下用什么

1. ConcurrentHashMap(首选)

JDK8:数组 + 链表/红黑树,CAS + synchronized 锁桶头。详见 ConcurrentHashMap 为什么放弃分段锁

2. Collections.synchronizedMap

包装一个 Map,每个方法 synchronized。性能差,不推荐。

3. Hashtable

全表锁,更差,已淘汰。

五、设计要点总结

  • 为什么容量是 2 的幂(n-1) & hash 替代取模。
  • 为什么负载因子 0.75:时间空间权衡。
  • 为什么链表长度 8 树化:泊松分布下概率极低。
  • 为什么 key 用 String/Integer:不可变,hashCode 稳定。

高频追问

  • HashMap 允许 null key/value,Hashtable/ConcurrentHashMap 不允许。
  • ConcurrentHashMap 不允许 null value,因为并发下"二义性":null 是不存在还是值就是 null?