1. 程式人生 > >HashTable —— 線程安全的散列表

HashTable —— 線程安全的散列表

機制 iter nth already size set 映射 city hold

HashTable 的一些認識:

  1.   底層使用散列表,存貯鍵值對,鍵值非null
  2. 使用synchronize 保證線程安全
  3.   如果多線程高發量,推薦使用 concurrentHashMap;如無需多線程,可使用 HashMap

■ 重要全局變量

//The hash table data.
//底層維護一個Entry(鍵值對)數組
private transient Entry<K,V>[] table;
//The total number of entries in the hash table. //元素總量,等同於HashMap的size private transient
int count;
//The load factor for the hashtable. //負載因子 private float loadFactor;
/** * The table is rehashed when its size exceeds this threshold. * (The value of this field is (int)(capacity * loadFactor).) * 超過閾值進行rehash */ private int threshold; /** * The number of times this Hashtable has been structurally modified * Structural modifications are those that change the number of entries in * the Hashtable or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the Hashtable fail-fast. (See ConcurrentModificationException). * 結構性變動時modCount計數+1,用於遍歷時的fail-fast機制生效
*/ private transient int modCount = 0

構造函數

/**
     * Constructs a new, empty hashtable with the specified initial
     * capacity and the specified load factor.
     */
    public Hashtable(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        
if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load: "+loadFactor); if (initialCapacity==0) initialCapacity = 1; //沒有強調2次冪,cap為0時,默認是1 this.loadFactor = loadFactor; table = new Entry<?,?>[initialCapacity]; threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); } /** * Constructs a new, empty hashtable with a default initial capacity (11) * and load factor (0.75). * 容量11,負載因子 0.75 */ public Hashtable() { this(11, 0.75f); }

■主要方法

- put(K key, V value)

public synchronized V put(K key, V value) {
    // Make sure the value is not null,而HashMap選擇將key為null永遠存放為table[0]位置
    if (value == null) {
        throw new NullPointerException();
    }
    // Makes sure the key is not already in the hashtable.
    //確保key不在hashtable中
    //首先,通過hash方法計算key的哈希值,並計算得出index值,確定其在table[]中的位置
    //其次,叠代index索引位置的鏈表,如果該位置處的鏈表存在相同的key,則替換value,返回舊的value
    Entry tab[] = table;
    int hash = hash(key);
    //計算下標,這裏使用%方法,性能遠不及HashMap的位運算 (這也是不推薦使用HashTable的原因之一)
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
        //直接使用equals比較,而HashMap多了一層地址比較 `((k = e.key) == key || key.equals(k))`
        if ((e.hash == hash) && e.key.equals(key)) {
            V old = e.value;
            e.value = value;
            return old;
        }
    }
//結構性變更操作 modCount計數+1 modCount++; //HashMap選擇新增addEntry方法封裝一下邏輯 if (count >= threshold) { // Rehash the table if the threshold is exceeded //如果超過閥值,就進行rehash操作 rehash(); tab = table; hash = hash(key); index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. //將值插入,返回的為null Entry<K,V> e = tab[index]; // 創建新的Entry節點,並將新的Entry插入Hashtable的index位置,並設置e為新的Entry的下一個元素 tab[index] = new Entry<>(hash, key, value, e); //size++ count++; return null; }

- get(Object key)

public synchronized V get(Object key) {
        Entry tab[] = table;   //臨時拷貝,保證數據的時效性
        int hash = hash(key);
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return e.value;
            }
        }
        return null;
    }

■ HashTable 遍歷

/**
 * Created by roman on 2017/10/03.
 *【2017 java‘s day】
 */
public class ListHashTableTest {
    public static void main(String[] args) {
        Hashtable<String, Object> hb = new Hashtable<String, Object>();
        hb.put("1", "Java");
        hb.put("flag", true);
        hb.put("age", 44);

        Iterator iterator = hb.keySet().iterator();
        iterator.forEachRemaining(value -> System.out.println(hb.get(value)));  //使用了lamda
    }
}

■ HashTable VS HashMap

  • HashTable 基於 Dictionary 類,而 HashMap 是基於 AbstractMap。Dictionary 是任何可將鍵映射到相應值的類的抽象父類,而 AbstractMap 是基於 Map 接口的實現,它以最大限度地減少實現此接口所需的工作.
  • HashMap 的 key 和 value 都允許為 null,而 Hashtable 的 key 和 value 都不允許為 null。HashMap 遇到 key 為 null 的時候,調用 putForNullKey 方法進行處理(統一放入table[0]位置),而對 value 沒有處理;Hashtable遇到 null,直接返回 NullPointerException.
  • Hashtable 方法是同步,而HashMap則不是。Hashtable 中的幾乎所有的 public 的方法都是 synchronized 的,而有些方法也是在內部通過 synchronized 代碼塊來實現。
  • HashTable由於使用sync和%運算(以及相關算法實現)的緣故,相比於HashMap,性能較低,因此非常不推薦繼續使用HashTable。
  • 非競爭環境下推薦使用HashMap。
  • 多線程環境下推薦使用ConcurrentHashMap。

HashTable —— 線程安全的散列表