Hashtable源碼分析_JDK1.8版本

Hashtable簡介

聲明

文章均爲本人技術筆記,轉載請註明出處https://segmentfault.com/u/yzwalljava

Hashtable聲明

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

Hashtable和HashMap同樣也是散列表,存儲元素也是鍵值對;
Hashtable繼承於Dictionary類(Dictionary類聲明瞭操做鍵值對的接口方法),實現Map接口(定義鍵值對接口);
Hashtable大部分類用synchronized修飾,證實Hashtable是線程安全的;數組

Hashtable基本數據結構

  • private transient Entry<?,?>[] table:鍵值對/Entry數組,每一個Entry本質上是一個單向鏈表的表頭安全

  • private int threshold:rehash閾值數據結構

  • private float loadFactor:裝填因子函數

  • private transient int modCount = 0: Hashtable結構化修改次數,用來實現fail-fast機制;線程

  • private transient volatile Set<Map.Entry<K,V>> entrySet:Hastable視圖,鍵值對集合;code

  • private transient volatile Set<K> keySetHastable視圖,key集,Hashtable中key不可重複;繼承

  • private transient volatile Collection<V> valuesHastable視圖,value集合,可重複;索引

    • Hashtable中key和value是一對多關係;

鍵值對/Entry

private static class Entry<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Entry<K, V> next;
    ...
    // 計算鍵值對的hashCode
    public int hashCode() {
        // "^" 按位異或, hash在調用構造器時傳入
        return hash ^ Objects.hashCode(value);
    }
}

Hash函數

鍵值對

Hashtable方法分析

public synchronized boolean contains(Object value)

public boolean containsValue(Object value)內部調用contains(value);

判斷是否含有該value的鍵值對,在Hashtable中hashCode相同的Entry用鏈表組織,hashCode不一樣的存儲在Entry數組table中;

// in contains() method.
Entry<?,?> tab[] = table;
// 查找:遍歷全部Entry鏈表
for (int i = tab.length ; i-- > 0 ;) {
    for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
        if (e.value.equals(value)) {
            return true;
        }
    }
}
return false;

public synchronized boolean containsKey(Object key)

散列分佈策略

int index = (hash & 0x7FFFFFFF) % tab.length;
計算鍵值對的桶位(本質是鍵值對在tab數組中的索引),Hashtable本質上採用除數取餘法進行散列分佈,模運算效率較低

Hash函數

int hash = key.hashCode() Hashtable直接調用key的hashCode()計算hashCode;

containsKey方法體
Entry<?,?> tab[] = table;
int hash = key.hashCode();
/**
 * 計算index, % tab.length防止數組越界
 * index表示key對應entry所在鏈表表頭
 */
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
    if ((e.hash == hash) && e.key.equals(key)) {
        return true;
    }
}
return false;

public synchronized V get(Object key):根據指定key查找對應value,查找原理與containsKey相同,查找成功返回value,不然返回null;

public synchronized V put(K key, V value)

設置鍵值對,key和value都不可爲null,設置順序:

  1. 若是Hashtable含有key,設置(key, oldValue) -> (key, newValue);

  2. 若是Hashtable不含有key, 調用addEntry(...)添加新的鍵值對;

private void addEntry(int hash, K key, V value, int index)

當鍵值對個數超過閾值,先進行rehash而後添加entry,不然直接添加entry;

public synchronized V remove(Object key)

remove操做,計算key所在鏈表表頭table[index],而後進行單向鏈表的節點刪除操做

public synchronized Object clone()

對Hashtable的淺拷貝操做,淺拷貝全部bucket(單向鏈表組織形式)的表頭;

protected void rehash()

當Hashtable中鍵值對總數超過閾值(容量*裝載因子)後,內部自動調用rehash()增長容量,從新計算每一個鍵值對的hashCode;
int newCapacity = (oldCapacity << 1) + 1計算新容量 = 2 * 舊容量 + 1;而且根據新容量更新閾值;

...
for (int i = oldCapacity ; i-- > 0 ;) {
    // 拷貝每一個Entry鏈表
    for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
        Entry<K,V> e = old;
        old = old.next;
        // 從新計算每一個Entry鏈表的表頭索引(rehash)
        int index = (e.hash & 0x7FFFFFFF) % newCapacity;
        // 開闢鏈表節點
        e.next = (Entry<K,V>)newMap[index];
        // 拷貝
        newMap[index] = e;
    }
}
相關文章
相關標籤/搜索