HashMap vs Hashtable:從源碼到實戰的全方位對比
作為 Java 集合框架中最經典的面試題,HashMap 與 Hashtable 的區別不僅在於「線程安全」與「允許 null」,更體現在繼承體系、擴容機制、哈希計算等底層細節。本文結合 JDK 源碼(以 JDK 8 為參考),為你拆解核心差異。
1. 類繼承體系不同
| 特性 | HashMap | Hashtable |
| 父類 | AbstractMap | Dictionary(舊集合類) |
| 實現接口 | Map, Cloneable, Serializable | Map, Cloneable, Serializable |
2. 同步與線程安全
- Hashtable:所有公共方法均加
synchronized,線程安全但性能較低。
- HashMap:非同步,高性能。多線程環境下可用
Collections.synchronizedMap() 包裝,或改用 ConcurrentHashMap。
3. Null 鍵值支持
- HashMap:允許
null key(最多一個)與 null value。
- Hashtable:
put 時若 key 或 value 為 null 直接拋 NullPointerException。
4. 源碼關鍵差異(JDK 8 視角)
Hashtable.put()
public synchronized V put(K key, V value) {
if (value == null) throw new NullPointerException(); // 不允許 null value
int hash = key.hashCode(); // key 不能為 null,否則 NPE
int index = (hash & 0x7FFFFFFF) % tab.length;
// ... 遍歷鏈表覆蓋或插入 ...
}
HashMap.put()
public V put(K key, V value) {
if (key == null) return putForNullKey(value); // 允許 null key
int hash = hash(key.hashCode()); // 經過擾動函數二次哈希
int i = indexFor(hash, table.length);
// ... 遍歷鏈表/紅黑樹覆蓋或插入 ...
}
5. 其他重要差異
- contains 方法:Hashtable 有
contains(Object value) 易混淆,HashMap 移除,改用 containsValue() 和 containsKey()。
- 迭代器:HashMap 迭代器是 fail-fast(修改計數器
modCount),Hashtable 的 Enumeration 不是。
- 初始容量與擴容:HashMap 默認 16,擴容 2 倍(冪次方);Hashtable 默認 11,擴容
2n+1(質數偏好)。
- 哈希計算:HashMap 引入
hash(Object key) 擾動函數(高 16 位參與運算),減少碰撞;Hashtable 直接用 key.hashCode()。
6. 總結對比表
| 維度 | HashMap | Hashtable |
| 線程安全 | 否 | 是(synchronized) |
| 允許 null key/value | 是 | 否 |
| 父類 | AbstractMap | Dictionary |
| 性能 | 高 | 較低 |
| 推薦場景 | 單線程、或配合 ConcurrentHashMap | 舊代碼維護,極少新建 |
最佳實踐建議
- 新代碼優先使用
HashMap(單線程)或 ConcurrentHashMap(多線程)。
- 避免在多線程中直接操作
HashMap,若需同步用 Collections.synchronizedMap(new HashMap<>())。
- 初始化時指定容量
new HashMap<>(expectedSize) 減少擴容開銷。