HashTable原理和底層實現

1. 概述java

上次討論了HashMap的結構,原理和實現,本文來對Map家族的另一個經常使用集合HashTable進行介紹。HashTable和HashMap兩種集合很是類似,常常被各類面試官問到二者的區別。面試

對於二者的區別,主要有如下幾點:數組

  1. HashMap是非同步的,沒有對讀寫等操做進行鎖保護,因此是線程不安全的,在多線程場景下會出現數據不一致的問題。而HashTable是同步的,全部的讀寫等操做都進行了鎖(synchronized)保護,在多線程環境下沒有安全問題。可是鎖保護也是有代價的,會對讀寫的效率產生較大影響。
  2. HashMap結構中,是容許保存null的,Entry.keyEntry.value都可覺得null。可是HashTable中是不容許保存null的。
  3. HashMap的迭代器(Iterator)是fail-fast迭代器,可是Hashtable的迭代器(enumerator)不是fail-fast的。若是有其它線程對HashMap進行的添加/刪除元素,將會拋出ConcurrentModificationException,但迭代器自己的remove方法移除元素則不會拋出異常。這條一樣也是Enumeration和Iterator的區別。

     

    2. 原理

    HashTable類中,保存實際數據的,依然是Entry對象。其數據結構與HashMap是相同的。
    图片描述
    HashTable類繼承自Dictionary類,實現了三個接口,分別是MapCloneablejava.io.Serializable,以下圖所示。安全

图片描述

HashTable中的主要方法,如putgetremoverehash等,與HashMap中的功能相同,這裏不做贅述,能夠參考另一篇文章HashMap實現原理及源碼分析數據結構

 

3. 源碼分析多線程

HashTable的主要方法的源碼實現邏輯,與HashMap中很是類似,有一點重大區別就是全部的操做都是經過synchronized鎖保護的。只有得到了對應的鎖,才能進行後續的讀寫等操做。源碼分析

 

1. put方法.net

put方法的主要邏輯以下:線程

  1. 先獲取synchronized鎖。
  2. put方法不容許null值,若是發現是null,則直接拋出異常。
  3. 計算key的哈希值和index
  4. 遍歷對應位置的鏈表,若是發現已經存在相同的hash和key,則更新value,並返回舊值。
  5. 若是不存在相同的key的Entry節點,則調用addEntry方法增長節點。
  6. addEntry方法中,若是須要則進行擴容,以後添加新節點到鏈表頭部。
public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        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;
    }




 private void addEntry(int hash, K key, V value, int index) {
        modCount++;

        Entry<?,?> tab[] = table;
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            rehash();

            tab = table;
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // Creates the new entry.
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>) tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }

2. get方法code

get方法的主要邏輯以下

  1. 先獲取synchronized鎖。
  2. 計算key的哈希值和index。
  3. 在對應位置的鏈表中尋找具備相同hash和key的節點,返回節點的value。
  4. 若是遍歷結束都沒有找到節點,則返回null
public synchronized V get(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        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 (V)e.value;
            }
        }
        return null;
    }

3.rehash擴容方法

rehash擴容方法主要邏輯以下:

  1. 數組長度增長一倍(若是超過上限,則設置成上限值)。
  2. 更新哈希表的擴容門限值。
  3. 遍歷舊錶中的節點,計算在新表中的index,插入到對應位置鏈表的頭部。
protected void rehash() {
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        // overflow-conscious code
        int newCapacity = (oldCapacity << 1) + 1;
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }
        Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

        modCount++;
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        table = newMap;

        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;

                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = (Entry<K,V>)newMap[index];
                newMap[index] = e;
            }
        }
    }

 

4. 總結

HashTable相對於HashMap的最大特色就是線程安全,全部的操做都是被synchronized鎖保護的

相關文章
相關標籤/搜索