一文搞懂HashMap工作原理和擴容機制(深度對比JDK1.7和JDK1.8)

HashMap簡介

  • HashMap基於哈希表的Map接口實現,是以key-value存儲形式存在.
  • 系統會根據hash算法來計算key-value的存儲位置,可以通過key快速存取value.
  • HashMap使用鏈表來解決碰撞問題,當發生碰撞了,對象將會儲存在鏈表的下一個節點中。 HashMap在每個鏈表節點中儲存鍵值對對象。
  • key相同的查找? 因爲key本身就是對象, 具有hashcode()和equas()方法, 所以先調用hashcode()方法, 定位到bucket(桶), 然後再調用鍵對象的equals()方法, 對應的屬性.

Entry 數據結構

Entry( int h, K k, V v, Entry<K,V> n) {
        value = v;
        next = n;
        key = k;
        hash = h;
}

HashMap的結構圖(1.7) Entry數組+鏈表

在這裏插入圖片描述

jdk1.8中廢除Entry

jdk1.6中,HashMap中有個內置Entry類,它實現了Map.Entry接口;
jdk1.8中,這個Entry類不見了,變成了Node類,也實現了Map.Entry接口,與jdk1.6中的Entry是等價的。

HashMap的結構圖(1.8+) Node數組+鏈表+紅黑樹

在這裏插入圖片描述
當鏈表長度大於8,Node數組結點轉爲紅黑樹

//e是p的下一個節點
if ((e = p.next) == null) {
    //插入鏈表的尾部
    p.next = newNode(hash, key, value, null);
    //如果插入後鏈表長度大於8則轉化爲紅黑樹
    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
        treeifyBin(tab, hash);
    break;
}

存儲原理

根據上面圖片, 我們可以看出, 如果 HashMap 的每個 bucket 裏只有一個 Entry 時,HashMap 可以根據索引、快速地取出該 bucket 裏的 Entry;在發生“Hash 衝突”的情況下,單個 bucket 裏存儲的不是一個 Entry,而是一個 Entry 鏈,系統只能必須按順序遍歷每個 Entry,直到找到想搜索的 Entry 爲止——如果恰好要搜索的 Entry 位於該 Entry 鏈的最末端(該 Entry 是最早放入該 bucket 中),那系統必須循環到最後才能找到該元素。
一句話: put(k,v), 調用hashcode() get(k)調用key.hashcode() 如果衝突有多個, 再調用key.equals(key2);

負載因子和擴容

initailCapacity * loadFactor = HashMap容量
當創建 HashMap 時,有一個默認的負載因子(load factor),其默認值爲 0.75,這是時間和空間成本上一種折衷:增大負載因子可以減少 Hash 表(就是那個 Entry 數組)所佔用的內存空間,但會增加查詢數據的時間開銷,而查詢是最頻繁的的操作(HashMap 的 get() 與 put() 方法都要用到查詢);減小負載因子會提高數據查詢的性能,但會增加 Hash 表所佔用的內存空間。

如果開始就知道 HashMap 會保存多個 key-value 對,可以在創建時就使用較大的初始化容量,如果 HashMap 中 Entry 的數量一直不會超過極限容量(capacity * load factor),HashMap 就無需調用 resize() 方法重新分配 table 數組,從而保證較好的性能。

HashMap的大小很簡單,不是實時計算的,而是每次新增加Entry/Node的時候,size就遞增。刪除的時候就遞減。當到達閾值(負載因子*總size)時, 容量翻倍, 並且永遠是2^n, 因爲hash取模運算太慢, 2^n的容量可以進行位運算

頭插尾插?

jdk 1.7 頭插
jdk 1.8+ 尾插

因爲hashmap在併發resize時會出現的死循環問題, 並且1.7時候用頭插是考慮到了一個所謂的熱點數據的點(新插入的數據可能會更早用到),但這其實是個僞命題, 因爲JDK1.7中rehash的時候,舊鏈表遷移新鏈表的時候,如果在新表的數組索引位置相同,則鏈表元素會倒置(就是因爲頭插) 所以最後的結果 還是打亂了插入的順序 所以總的來看支撐1.7使用頭插的這點原因也不足以支撐下去了 所以就乾脆換成尾插 一舉多得

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章