JDK1.8中對hashmap的優化


 

Java編程語言中,最基本的結構就是兩種,一個是數組,另外一個是模擬指針(引用),所有的數據結構都可以用這兩個基本結構來構造的,HashMap也不例外。HashMap實際上是一個“鏈表散列”的數據結構,即數組和鏈表的結構,但是在jdk1.8里 
加入了紅黑樹的實現,當鏈表的長度大於8時,轉換為紅黑樹的結構。

 

Java中HashMap采用了鏈地址法。鏈地址法,簡單來說,就是數組加鏈表的結合。在每個數組元素上都一個鏈表結構,當數據被Hash后,得到數組下標,把數據放在對應下標元素的鏈表上。

     */
    static class Node<K,V> implements Map.Entry<K,V> { final int hash;//用於定位數組索引的位置 final K key; V value; Node<K,V> next;//鏈表的下一個Node Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; }

 

Node是HashMap的一個內部類,實現了Map.Entry接口,本質是就是一個映射(鍵值對)。

有時兩個key會定位到相同的位置,表示發生了Hash碰撞。當然Hash算法計算結果越分散均勻,Hash碰撞的概率就越小,map的存取效率就會越高。

HashMap類中有一個非常重要的字段,就是 Node[] table,即哈希桶數組,明顯它是一個Node的數組。如果哈希桶數組很大,即使較差的Hash算法也會比較分散,如果哈希桶數組數組很小,即使好的Hash算法也會出現較多碰撞,所以就需要在空間成本和時間成本之間權衡,其實就是在根據實際情況確定哈希桶數組的大小,並在此基礎上設計好的hash算法減少Hash碰撞。那么通過什么方式來控制map使得Hash碰撞的概率又小,哈希桶數組(Node[] table)占用空間又少呢?答案就是好的Hash算法和擴容機制。

 

如果哈希桶數組很大,即使較差的Hash算法也會比較分散,如果哈希桶數組數組很小,即使好的Hash算法也會出現較多碰撞,所以就需要在空間成本和時間成本之間權衡,其實就是在根據實際情況確定哈希桶數組的大小,並在此基礎上設計好的hash算法減少Hash碰撞。

這里存在一個問題,即使負載因子和Hash算法設計的再合理,也免不了會出現拉鏈過長的情況,一旦出現拉鏈過長,則會嚴重影響HashMap的性能。於是,在JDK1.8版本中,對數據結構做了進一步的優化,引入了紅黑樹。而當鏈表長度太長(默認超過8)時,鏈表就轉換為紅黑樹,利用紅黑樹快速增刪改查的特點提高HashMap的性能,其中會用到紅黑樹的插入、刪除、查找等算法

 

 

  • 由 數組+鏈表 的結構改為 數組+鏈表+紅黑樹
    拉鏈過長會嚴重影響hashmap的性能,所以1.8的hashmap引入了紅黑樹。
    在鏈表元素數量超過8時改為紅黑樹,少於6時改為鏈表,中間7不改是避免頻繁轉換降低性能。
    相對於鏈表,改為紅黑樹后碰撞元素越多查詢效率越高。鏈表O(n),紅黑樹O(logn)。
  • 優化了高位運算的hash算法:h^(h>>>16)
    將hashcode無符號右移16位,讓高16位和低16位進行異或。
  • 擴容后,元素要么是在原位置,要么是在原位置再移動2次冪的位置,且鏈表順序不變。
    不需要重新計算hash,只需要根據原來hash值新增的bit是1還是0分別放進兩個鏈表lo和hi(非紅黑樹的情況)里,0的話索引沒變,1的話索引變為原索引加原來的數組長度。
    因為用的尾插法所以新數組鏈表不會倒置,多線程下不會出現死循環。


作者:pluss
鏈接:https://www.jianshu.com/p/a3a0090fac4d
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM