java-map之Hashtable
阿新 • • 發佈:2020-08-09
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的整數次冪。