1. 程式人生 > 實用技巧 >java-map之Hashtable

java-map之Hashtable

1.1 概述

HashTable也是一個散列表,它儲存的內容是鍵值對對映。HashTable繼承於Dictionary,實現了Map、Cloneable、java.io.Serializable介面。HashTable的函式都是同步的,這意味著它是執行緒安全的。它的Key、Value都不可以為null。此外,HashTable中的對映不是有序的。

1.2詳解

//為一個Entry[]陣列型別,Entry代表了“拉鍊”的節點,每一個Entry代表了一個鍵值對,雜湊表的"key-value鍵值對"都是儲存在Entry陣列中的。
private transient Entry<?,?>[] table;

//HashTable的大小,注意這個大小並不是HashTable的容器大小,而是他所包含Entry鍵值對的數量。
private transient int count;

//Hashtable的閾值,用於判斷是否需要調整Hashtable的容量。threshold的值=“容量*載入因子”
private int threshold;

//載入因子。
private float loadFactor;

//用來實現“fail-fast”機制的(也就是快速失敗)。所謂快速失敗就是在併發集合中,其進行迭代操作時,若有其他執行緒對其進行結構性的修改,這時迭代器會立馬感知到,並且立即丟擲ConcurrentModificationException異常,而不是等到迭代完成之後才告訴你(你已經出錯了)
private transient int modCount = 0;

  • put方法
    1.獲取synchronized鎖。
    2.put方法不允許null值,如果發現是null,則直接丟擲異常。
    3.計算key的雜湊值和index
    4.遍歷對應位置的連結串列,如果發現已經存在相同的hash和key,則更新value,並返回舊值。
    5.如果不存在相同的key的Entry節點,則呼叫addEntry方法增加節點。
//獲取synchronized鎖
public synchronized V put(K key, V value) {
    // Make sure the value is not null
    //如果value是空丟擲異常
    if (value == null) {
        throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    //計算key的雜湊值和index
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    //遍歷對應位置列表
    for(; entry != null ; entry = entry.next) {
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }

    addEntry(hash, key, value, index);
    return null;
}

  • get方法
    1.先獲取synchronized鎖。
    2.計算key的雜湊值和index。
    3.在對應位置的連結串列中尋找具有相同hash和key的節點,返回節點的value。
    4.如果遍歷結束都沒有找到節點,則返回null。
public synchronized V get(Object key) {//根據鍵取出對應索引  
      Entry tab[] = table;  
      int hash = hash(key);//先根據key計算hash值  
      int index = (hash & 0x7FFFFFFF) % tab.length;//再根據hash值找到索引  
      for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {//遍歷entry鏈  
          if ((e.hash == hash) && e.key.equals(key)) {//若找到該鍵  
              return e.value;//返回對應的值  
          }  
      }  
      return null;//否則返回null  
  }  

  • remove方法
    1.先獲取synchronized鎖。
    2.計算key的雜湊值和index。
    3.遍歷對應位置的連結串列,尋找待刪除節點,如果存在,用e表示待刪除節點,pre表示前驅節點。如果不存在,返回null。
    4.更新前驅節點的next,指向e的next。返回待刪除節點的value值。
public synchronized V remove(Object key) {
    Entry<?,?> tab[] = table;
    //計算key的雜湊值和index
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>)tab[index];
    for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
        //遍歷對應位置的連結串列,尋找待刪除節點
        if ((e.hash == hash) && e.key.equals(key)) {
            modCount++;
			//更新前驅節點的next,指向e的next
            if (prev != null) {
                prev.next = e.next;
            } else {
                tab[index] = e.next;
            }
            count--;
            V oldValue = e.value;
            e.value = null;
            //返回待刪除節點的value值
            return oldValue;
        }
    }
    //如果不存在,返回null
    return null;
}

和hashmap的主要區別:

  • hashtable支援併發程式設計,是執行緒安全的。
    主要是因為其底層使用了synchronized關鍵字。但是這樣犧牲了效率。當需要多執行緒操作的時候也可以使用執行緒安全的ConcurrentHashMap。ConcurrentHashMap雖然也是執行緒安全的,但是它的效率比Hashtable要高好多倍。因為ConcurrentHashMap使用了分段鎖,並不對整個資料進行鎖定。
  • hashtable不支援key和value為空。當出翔put(null,null)的時候會出現空指標異常。
  • 擴容機制和hash值不同。
    Hashtable直接使用Object的hashCode(),hashCode是JDK根據物件的地址或者字串或者數字算出來的int型別的數值,然後再使用去取模運算來獲得最終的位置。兩者的預設負載因子都是0.75,但Hashtable擴容時,容量變為原來的2倍+1,HashMap擴容時,將容量變成原來的2倍;Hashtable在不制定容量的情況下預設容量是11,也就是說Hashtable會盡量使用素數、奇數,而HashMap 的預設容量 為16,Hashtable不要求底層陣列的容量為2的整數次冪,而 HashMap 要求一定為2的整數次冪。