JDK1.8中HashMap的hash算法和尋址算法


JDK 1.8 中 HashMap 的 hash 算法和尋址算法

HashMap 源碼

hash() 方法

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

h = key.hashCode() 表示 h 是 key 對象的 hashCode 返回值;

h >>> 16 是 h 右移 16 位,因為 int 是 4 字節,32 位,所以右移 16 位后變成:左邊 16 個 0 + 右邊原 h 的高 16 位;

最后把這兩個進行異或返回。

異或:二進制位運算。如果一樣返回 0,不一樣則返回 1。

例:兩個二進制 110 和 100 進行異或

​ 110

^ 100

結果= 010

putVal() 中尋址部分

tab[i = (n - 1) & hash]

tab 就是 HashMap 里的 table 數組 Node<K,V>[] table

n 是這個數組的長度 length;

hash 就是上面 hash() 方法返回的值;

為什么不直接用 hashCode() % length ?

看完源碼會有疑問,為什么不直接用 key 對象的 hashCode 對哈希表長度取模?

尋址為什么不用取模?

對於上面尋址算法,由於計算機對比取模,與運算會更快。所以為了效率,HashMap 中規定了哈希表長度為 2 的 k 次方,而 2^k-1 轉為二進制就是 k 個連續的 1,那么 hash & (k 個連續的 1) 返回的就是 hash 的低 k 個位,該計算結果范圍剛好就是 0 到 2^k-1,即 0 到 length - 1,跟取模結果一樣。

也就是說,哈希表長度 length 為 2 的整次冪時, hash & (length - 1) 的計算結果跟 hash % length 一樣,而且效率還更好。

為什么不直接用 hashCode() 而是用它的高 16 位進行異或計算新 hash 值?

int 類型占 32 位,可以表示 2^32 種數(范圍:-2^31 到 2^31-1),而哈希表長度一般不大,在 HashMap 中哈希表的初始化長度是 16(HashMap 中的 DEFAULT_INITIAL_CAPACITY),如果直接用 hashCode 來尋址,那么相當於只有低 4 位有效,其他高位不會有影響。這樣假如幾個 hashCode 分別是 210、220、2^30,那么尋址結果 index 就會一樣而發生沖突,所以哈希表就不均勻分布了。

為了減少這種沖突,HashMap 中讓 hashCode 的高位也參與了尋址計算(進行擾動),即把 hashCode 高 16 位與 hashCode 進行異或算出 hash,然后根據 hash 來做尋址。

JDK 源碼中 HashMap 的 hash 方法原理是什么?


免責聲明!

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



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