用过哪些 Map 类?有什么区别?HashMap 并发下用什么?¶
一、常见 Map 实现¶
| Map | 线程安全 | 有序 | 特点 |
|---|---|---|---|
| HashMap | ❌ | 无序 | 最常用 |
| LinkedHashMap | ❌ | 插入顺序 / 访问顺序 | 继承 HashMap |
| TreeMap | ❌ | 按 key 排序 | 红黑树 |
| Hashtable | ✅(全表锁) | 无序 | 遗留类 |
| ConcurrentHashMap | ✅ | 无序 | 高并发 |
| ConcurrentSkipListMap | ✅ | 按 key 排序 | 并发版 TreeMap |
| EnumMap | ❌ | 按枚举顺序 | key 是枚举 |
| WeakHashMap | ❌ | 无序 | 弱引用 key |
二、HashMap 内部原理¶
存储结构¶
JDK8:数组 + 链表 + 红黑树。
默认容量¶
16,必须是 2 的幂。
hash 计算¶
扰动函数,让高位也参与下标计算。
扩容¶
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?