HashMap的hash算法和尋址算法怎麼優化的(JDK1.8)

Map 中 put 和 get 的優化

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
舉個例子
key="ykx"
key的hashCode是=119718  這是個10進制數
原始hashCode對應二進制11101001110100110==00000000000000011101001110100110
然後我再向右位移16位後的二進制==00000000000000000000000000000001 
異或運算
    0000000000000001 1101001110100110
    0000000000000000 0000000000000001
    0000000000000001 1101001110100111==119719 這個就是最終的hash值

優化點在哪

HashMap在get方法去獲取值時是這樣的
    hash(key)&(n-1) 進行與運算而找到數組位置
    0000000000000001 1101001110100111 上面優化後的hash值 hashMap默認數組長度16
    0000000000000000 0000000000001111 &
    0000000000000000 0000000000000111 =7
    這就是最終的位置下標  只要數組長度是2的n次方  你會發現:119719%16119719&(16-1)的值時相同的

因爲與運算的性能會比取模效率高。

總結

其實這裏最重要的幾個點就是在算hash值時高是16位與低16進行了異或運算(因爲很有可能有兩個不同的值hash高16位不相同低16位相同)。這樣就導致hashmap尋址運算時低16位已經包含了高16位與低16 的特徵,因爲在尋址的時候大多數都是低16位在運算,因爲數組長度-1的數字大小一般情況都比較小。所以在get尋址時基本都是低16在運算,儘量避免hash衝突,尋址時用與運算代替取模運算也是比較大的優化,只要當hashmap的數組長度是2的n次方那麼我們算出來的hash值取模這個長度等於與運算這個長度-1 的值。。

如果說衝突了會怎樣

當hash衝突時會在當前下標建立一個鏈表來維護。jdk爲了維護在遍歷時性能夠好,當鏈表長度大於8的時候迴轉化爲紅黑樹,這時查詢的時間複雜度就是O(logn)

爲什麼是8而不是4:我的理解是log2(8)=3而log2(4)=2 由於是紅黑樹、B+tree等 一般都是3層性能就是折中方案。

HashMap擴容後怎麼重新定義位置

默認是16長度,以後每次擴容時 都會重新遍歷原來的值拿到hash與 新的數組長度-1 進行與運算放到新的位置。

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