hashmap訪問時,都需要計算當前key應該對應entry陣列哪個元素,即計算陣列下標;演算法如下:
* returns index for hash code h.
staticintindexfor(inth,intlength) {
returnh & (length-1);
hashmap為了訪問高效,要盡量較少碰撞,就是要盡量把資料分配均勻,每個鍊錶長度大致相同,這個實現就在把資料存到哪個鍊錶中的演算法;
這個演算法實際就是取模,hash%length,計算機中直接求餘效率不如位移運算,原始碼中做了優化hash&(length-1),
hash%length==hash&(length-1)的前提是length是2的n次方;
為什麼這樣能均勻分布減少碰撞呢?2的n次方實際就是1後面n個0,2的n次方-1 實際就是n個1;
例如長度為9時候,3&(9-1)=0 2&(9-1)=0 ,都在0上,碰撞了;
例如長度為8時候,3&(8-1)=3 2&(8-1)=2 ,不同位置上,不碰撞;
其實就是按位「與」的時候,每一位都能 &1 ,也就是和1111……1111111進行與運算
0000 0011 3
& 0000 1000 8
= 0000 0000 0
0000 0010 2
& 0000 1000 8
= 0000 0000 0
0000 0011 3
& 0000 0111 7
= 0000 0011 3
0000 0010 2
& 0000 0111 7
= 0000 0010 2
當然如果不考慮效率直接求餘即可(就不需要要求長度必須是2的n次方了);
有人懷疑兩種運算效率差別到底有多少,我做個測試:
/***
* *
* 直接【求餘】和【按位】運算的差別驗證
* */
public static void main(string args) {
long currenttimemillis = system.currenttimemillis();
int a = 0;
int times = 10000 * 10000;
for (long i = 0; i < times; i++) {
a = 9999 % 1024;
long currenttimemillis2 = system.currenttimemillis();
int b = 0;
for (long i = 0; i < times; i++) {
b = 9999 & (1024 - 1);
long currenttimemillis3 = system.currenttimemillis();
system.out.println(a + "," + b);
system.out.println("%: " + (currenttimemillis2 - currenttimemillis));
system.out.println("&: " + (currenttimemillis3 - currenttimemillis2));
結果:783,783
%: 359
&: 93
jdk1.8 優化
hashmap在jdk1.8及以後的版本中引入了紅黑樹結構,若桶中鍊錶元素個數大於等於8時,鍊錶轉換成樹結構;若桶中鍊錶元素個數小於等於6時,樹結構還原成鍊錶。因為紅黑樹的平均查詢長度是log(n),長度為8的時候,平均查詢長度為3,如果繼續使用鍊錶,平均查詢長度為8/2=4,這才有轉換為樹的必要。鍊錶長度如果是小於等於6,6/2=3,雖然速度也很快的,但是轉化為樹結構和生成樹的時間並不會太短。
還有選擇6和8,中間有個差值7可以有效防止鍊錶和樹頻繁轉換。假設一下,如果設計成煉表個數超過8則鍊錶轉換成樹結構,鍊錶個數小於8則樹結構轉換成鍊錶,如果乙個hashmap不停的插入、刪除元素,鍊錶個數在8左右徘徊,就會頻繁的發生樹轉鍊錶、鍊錶轉樹,效率會很低。
HashMap的長度為什麼要是2的n次方
hashmap訪問時,都需要計算當前key應該對應entry陣列哪個元素,即計算陣列下標 演算法如下 returns index for hash code h.staticintindexfor inth,intlength returnh length 1 hashmap為了訪問高效,要盡量較少...
HashMap的長度為什麼要是2的N次方
先貼一段原始碼壓壓驚,the default initial capacity must be a power of two.static final int default initial capacity 1 4 aka 16見上面的原始碼中的注釋,must be a power of two ...
HashMap 的長度為什麼是2的冪次方
為了能讓 hashmap 訪問高效,盡量較少碰撞,也就是要盡量把資料分配均勻。我們上面也講到了過了,hash 值的範圍值 2147483648到2147483647,前後加起來大概40億的對映空間,只要雜湊函式對映得比較均勻鬆散,一般應用是很難出現碰撞的。但問題是乙個40億長度的陣列,記憶體是放不下...