Hashtable:遗留的全同步哈希表
一句话警告
Hashtable 已被废弃。它是 JDK 1.0 的遗留代码,性能差、设计保守。现代 Java 代码永远不要用 Hashtable。
Hashtable vs HashMap:有什么区别
Hashtable 和 HashMap 几乎一模一样,但有三个关键区别:
1. 线程安全的方式不同
java
// Hashtable:所有方法都 synchronized
public class Hashtable<K, V> {
public synchronized V put(K key, V value) { ... }
public synchronized V get(Object key) { ... }
public synchronized V remove(Object key) { ... }
// 等等...所有方法都加锁
}
// HashMap:方法不带 synchronized
public class HashMap<K, V> {
public V put(K key, V value) { ... } // 没有 synchronized
public V get(Object key) { ... } // 没有 synchronized
}2. null 的处理不同
java
Hashtable<String, Integer> ht = new Hashtable<>();
ht.put(null, 1); // ❌ NullPointerException
ht.put("a", null); // ❌ NullPointerException
HashMap<String, Integer> hm = new HashMap<>();
hm.put(null, 1); // ✅ OK
hm.put("a", null); // ✅ OK3. 初始容量和扩容策略不同
| 特性 | Hashtable | HashMap |
|---|---|---|
| 默认初始容量 | 11 | 16 |
| 扩容方式 | capacity * 2 + 1(奇数) | capacity * 2(翻倍) |
| 负载因子 | 0.75 | 0.75 |
为什么 Hashtable 被废弃
问题 1:全局锁性能差
Hashtable 的所有操作共用一把锁。高并发下,所有线程排队等锁:
java
// 10 个线程同时读同一个 Hashtable
// 线程 1 在 get()
// 线程 2 在 put()
// 线程 3 想 get()
// → 线程 3 必须等线程 1 或线程 2 释放锁才能操作性能对比:
10 线程并发写入 100 万元素:
Hashtable: ~5000 ms ← 全局锁,串行化
ConcurrentHashMap: ~200 ms ← 分段锁,并行化
快了 25 倍问题 2:迭代器是 fail-fast 的
Hashtable 的迭代器不支持并发修改,遍历过程中任何线程的修改都会抛出 ConcurrentModificationException。
问题 3:API 设计保守
- 不允许 null key 和 null value(很多场景不方便)
- 很多现代 API(如
computeIfAbsent)都没有
如何迁移
从 Hashtable 迁移到 ConcurrentHashMap
java
// ❌ 旧代码
Hashtable<String, Integer> old = new Hashtable<>();
old.put("a", 1);
old.get("a");
// ✅ 新代码
ConcurrentHashMap<String, Integer> modern = new ConcurrentHashMap<>();
modern.put("a", 1);
modern.get("a");
// ✅ 如果不需要并发安全,直接用 HashMap
Map<String, Integer> simple = new HashMap<>();
simple.put("a", 1);
simple.get("a");并发场景的选择
| 场景 | 推荐 |
|---|---|
| 单线程 | HashMap |
| 偶尔并发 | Collections.synchronizedMap(new HashMap<>()) |
| 高并发 | ConcurrentHashMap |
| 高并发 + 需要排序 | ConcurrentSkipListMap |
常见误解
误解 1:Hashtable 是线程安全的,所以更好
「线程安全」不等于「更好」。Hashtable 的全局锁在高并发下反而成为瓶颈。
误解 2:Hashtable 比 HashMap 更可靠
两者都是成熟的实现,都正确实现了 Map 接口。但 HashMap 有更好的性能和更现代的 API。
误解 3:遗留代码必须用 Hashtable
即使维护遗留代码,也应该逐步迁移到 ConcurrentHashMap。
总结
| 特性 | Hashtable | HashMap |
|---|---|---|
| 线程安全 | ✅ 全局锁 | ❌ 不安全 |
| null 支持 | ❌ 都不允许 | ✅ key/value 都可 null |
| 默认容量 | 11 | 16 |
| 扩容方式 | *2+1 | *2 |
| 推荐程度 | ❌ 不推荐 | ✅ 推荐 |
一句话:Hashtable 是 JDK 1.0 的产物,早该退出历史舞台了。
