首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java集合深度解析:Hashtable 同步機制、Enumeration 遍歷與遷移建議

     編輯:Java 相關知識

Hashtable 是 JDK 1.0 時代引入的經典哈希表,繼承 Dictionary、實現 Map、Cloneable、Serializable。它採用拉鏈法解決衝突,所有公開方法均聲明 synchronized,實現線程安全。但其鎖粒度粗(整表鎖)、不支持 null 鍵值、擴容策略固定(newCapacity = old*2+1),在高併發與現代開發中已逐漸被 ConcurrentHashMap 取代。理解其內部機制有助於維護舊代碼與掌握同步集合演進脈絡。

一、核心數據結構與同步模型

public class Hashtable<K,V> extends Dictionary<K,V> 
    implements Map<K,V>, Cloneable, java.io.Serializable {

    private transient Entry<K,V>[] table; // 條目數組
    private transient int count;           // 實際元素數
    private int threshold;                 // 擴容閾值
    private float loadFactor;              // 加載因子,默認 0.75f
    private transient int modCount = 0;    // 修改次數,用於 fail-fast

    private static class Entry<K,V> implements Map.Entry<K,V> {
        int hash; K key; V value; Entry<K,V> next;
        // 構造器、clone、getKey、getValue、setValue(拒絕 null)、equals、hashCode...
    }
}

所有公共方法(get、put、remove、size、containsKey…)均加 synchronized,等同於「全表鎖」。

二、關鍵操作源碼解析

1. 索引計算與查找

int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % table.length; // 正數化後取模

遍歷鏈表匹配 hash 且 key.equals。

2. Put 流程

public synchronized V put(K key, V value) {
    if (value == null) throw new NullPointerException(); // 拒絕 null 值
    // 1. 鍵存在 → 覆蓋值
    // 2. 鍵不存在 → 檢查擴容 → 頭插法插入新 Entry → count++
}

3. 擴容

protected void rehash() {
    int oldCapacity = table.length;
    int newCapacity = oldCapacity * 2 + 1; // 固定策略,結果為奇數
    Entry[] newMap = new Entry[newCapacity];
    // 重新哈希所有條目到新數組
    table = newMap;
    threshold = (int)(newCapacity * loadFactor);
}

擴容時機:count >= threshold。注意 rehash 非 final,子類可覆寫。

4. Remove 流程

標準單向鏈表刪除,維護 prev 指針,更新 modCount、count,斷開引用助 GC。

三、Enumeration 與 Iterator 雙重遍歷體系

Hashtable 同時支持舊式 Enumeration 與新式 Iterator,核心實現類 Enumerator 實現兩個接口:

private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
    // 從 table 末尾向前掃描,鏈表內向後遍歷
    public boolean hasMoreElements() { ... }
    public T nextElement() { ... }
    public boolean hasNext() { return hasMoreElements(); }
    public T next() { 
        if (modCount != expectedModCount) throw new ConcurrentModificationException();
        return nextElement(); 
    }
    public void remove() { // 僅 Iterator 模式支持
        if (!iterator) throw new UnsupportedOperationException();
        // 同步刪除 lastReturned 條目
    }
}

遍歷順序:數組索引遞減 + 鏈表順序,與插入順序無關。

四、集合視圖與同步包裝

keySet()、entrySet()、values() 返回的集合均通過 Collections.synchronizedSet/Collection 包裝,保證視圖操作同步:

public Set<K> keySet() {
    if (keySet == null) keySet = Collections.synchronizedSet(new KeySet(), this);
    return keySet;
}

內部類 KeySet、EntrySet、ValueCollection 分別繼承 AbstractSet/AbstractCollection,委託給外部 Hashtable 方法實現。

五、序列化與 Clone

  • writeObject:寫入容量、大小、所有鍵值對(數組逆序+鏈表順序)
  • readObject:按讀取順序重建,容量計算公式 (elements * loadFactor) + elements/20 + 3,確保為奇數
  • clone():深拷貝 table 數組及每條鏈表(遞歸 Entry.clone())

六、遍歷方式對比

方式適用場景線程安全性能
entrySet().iterator()需鍵值對,現代代碼首選同步視圖 + fail-fast高
keySet().iterator()僅需鍵同步視圖 + fail-fast中(額外 get)
values().iterator()僅需值同步視圖 + fail-fast高
keys() / elements()舊代碼維護Enumeration 無 fail-fast中

七、遷移建議:從 Hashtable 到 ConcurrentHashMap

為什麼遷移?

  • 全表鎖嚴重限制併發吞吐
  • 不支持 null 鍵值,業務受限
  • 擴容策略固定,無法調優
  • JDK 8+ ConcurrentHashMap 採用 CAS + synchronized 節點鎖 + 紅黑樹轉換,性能碾壓

遷移對照表

HashtableConcurrentHashMap備註
synchronized put/get無鎖 get、分段/節點鎖 put並發度提升數量級
拒絕 null允許 null 值(鍵仍不可 null)業務更靈活
Enumeration弱一致性 Iterator遍歷不拋 CME
Collections.synchronizedMap原生並發無需額外包裝

代碼對比

// 舊代碼
Hashtable<String, Integer> table = new Hashtable<>();
table.put("a", 1);

// 新代碼
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
// 原子複合操作
map.computeIfAbsent("key", k -> expensiveCompute());
map.merge("key", 1, Integer::sum);

八、總結

Hashtable 以「全方法同步」實現線程安全,結構簡單但擴展性差。其 Enumerator 雙模式遍歷、rehash 奇數擴容、集合視圖同步包裝等設計細節,反映了早期 JDK 的工程權衡。現代項目強烈建議遷移至 ConcurrentHashMap,獲得更高併發性能、更豐富原子操作 API 與弱一致性迭代器。僅在維護不可升級的舊系統時繼續使用 Hashtable,並注意其不支持 null、迭代器 fail-fast、複合操作需外部加鎖等限制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved