publicv put(k key, v value)
在putval方法之前,對 key 進行了 hash 計算。
staticfinal
inthash(object key)
通過 hashcode() 方法和無符號左移16後的 hashcode 進行異或計算。 進行了一次擾動計算。
再看 putval 方法中。如何計算 key 再陣列中的下標。
1final v putval(int hash, k key, v value, boolean
onlyifabsent,
2boolean
evict)
23if (e.hash == hash &&
24 ((k = e.key) == key || (key != null &&key.equals(k))))
25break
;26 p =e;27}
28}29if (e != null) 36}
37 ++modcount;
38if (++size >threshold)
39resize(); // 擴容
40afternodeinsertion(evict);
41return
null
;42 }
在第 6 行中, tab[i = (n - 1) & hash] 計算下標,使用了 hash 值跟 n-1 進行位與運算。
第一次初始容量為 16, hash 值跟 15 進行位 與運算。而 15 的二進位制是 1111。得到的是 hash 值的前 4位二進位制。所以得到的結果就是,0-15之間的數字,正好是陣列下標。
假如容量已經擴充套件,那現在的容量為 2n,hash 值跟 2n-1 進行位 與運算, 得到的是 hash 值的前 n 位二進位制。
原本的擴容方式就是將陣列變長,對每個節點資料重新計算下標,但是1.8版並不是這樣做的。看看原始碼:
1final node resize()
11else
if ((newcap = oldcap << 1) < maximum_capacity &&
12 oldcap >=default_initial_capacity)
13 newthr = oldthr << 1; //
double threshold // 否則,將原陣列大小擴充一倍14}
15else
if (oldthr > 0) //
initial capacity was placed in threshold
16 newcap =oldthr;
17else
21if (newthr == 0)
26 threshold =newthr;
27 @suppresswarnings()
28 node newtab = (node)new
node[newcap];
29 table =newtab;
30if (oldtab != null
) 52
else
59 } while ((e = next) != null
);60
if (lotail != null
) 64
if (hitail != null
) 68}69
}70}71
}72return
newtab;
73 }
新版 hashmap 對擴容進行了優化,當容量擴充為原來的 2 倍時,只需判斷新增的一位 bit 是0還是1。
例如,當由 16 擴充至 32 時,16 的 二進位制時 10000,與 hash 計算,得到的是第 5 位 bit 的值。
原本計算下標,只是與前 4 位進行與運算,現在擴容一次後是對前 5 位進行與運算。
擴容方法裡面並沒有重新計算所有位的與運算,只需判斷新增的第 5 位。減少了計算量。
HashMap在JDK1 7和1 8版本的區別
在jdk1.7中hashmap是以entry陣列來儲存資料。用key的hashcode取模來決定key會被放在陣列裡的位置 如果hashcode相同,或者hashcode取模結果相同 那麼這些key會被定義到entry陣列的同乙個格仔裡,這些key會形成乙個鍊錶。1.在jdk1.8中hashmap是...
ubuntu18 04版本換源
1 備份cp etc apt sources.list etc apt sources.list.bak2 編輯原始檔vim etc apt sources.list3 更換原始檔 刪除sources.list檔案的內容,選擇以下的乙個源新增到sources.list檔案裡 vim的使用 1 esc...
基於 JDK1 7 版本實現 HashMap
在jdk1.7中是用的 陣列 單鏈表實現的hashmap 前一篇我用了linkedlist 陣列實現,其實本質上差不多,只是沒有寫擴容這一塊的內容,今天來個原生的方式實現hashmap。hashmap擴容機制?擴容後會產生什麼影響?因為很多解釋都寫到注釋裡面了,就不分模組解釋了。hashmap只允許...