1、前言
??在回答這個問題之前,我們可以回顧一下HashMap的存取過程,當(dāng)執(zhí)行putVal的操作的時候,
首先檢查大小,看是否需要擴(kuò)容(默認(rèn)元素超過最大值的0.75時擴(kuò)容),如果需要擴(kuò)容就進(jìn)行擴(kuò)容
然后計算出key的hashcode,根據(jù)hashcode定位數(shù)值所在的bucketIndex
如果該位置上沒有元素,就直接插入,結(jié)束
如果該位置上有元素就使用equal比較是否相同
如果key相同就把新的value替換舊的value,結(jié)束
如果key不同,就繼續(xù)遍歷,找到根節(jié)點(diǎn),如果沒找到key的話,就構(gòu)造一個新的節(jié)點(diǎn),然后把節(jié)點(diǎn)插入到鏈表尾部,表示put成功(jdk 1.8 之后鏈表長度超過閾值就會轉(zhuǎn)化為紅黑樹)
2、詳解
??好了,一個put操作就這樣完成了,按照我們理想的狀況,hashMap的存取就是O(1),也就是直接根據(jù)hashcode就可以找到它,每個bucket只存儲一個節(jié)點(diǎn),鏈表指向都是null,這樣就比較開心了,不要出現(xiàn)一個鏈表很長的情況。
??所以我們希望它能分布的均勻一點(diǎn),如果讓我們設(shè)計的話,我們肯定是直接對長度取模-----hashcode % length,但HashMap的設(shè)計者卻不是這樣寫的,它寫成了2進(jìn)制運(yùn)算,如下:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
index = (n - 1) & hash
??為什么設(shè)計成(n - 1) & hash 這樣呢?在 n 為 2次冪的情況下時,(n - 1) & hash ≈ hash % n ,因?yàn)?進(jìn)制的運(yùn)算速度遠(yuǎn)遠(yuǎn)高于取模,所以就使用了這種方式,所以要求為2的冪。
??我們可以看到它求hash的過程,將32位的hashCode值向右移動16位,高位補(bǔ)0,也就是只要了高16位,這是為什么呢?因?yàn)閔ashcode的計算方法導(dǎo)致哈希值的差異主要在高位,而 (n - 1) & hash是忽略了容量以上的高位的,所以 使用h >>>16就是為了避免類似情況的哈希沖突