HashMap的長度為什麼要是2的n次方

2021-08-25 14:21:55 字數 2210 閱讀 7247

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次方

先貼一段原始碼壓壓驚,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的n次方

hashmap訪問時,都需要計算當前key應該對應entry陣列哪個元素,即計算陣列下標 演算法如下 returns index for hash code h.staticintindexfor inth,intlength returnh length 1 hashmap為了訪問高效,要盡量較少...

HashMap 的長度為什麼是2的冪次方

為了能讓 hashmap 訪問高效,盡量較少碰撞,也就是要盡量把資料分配均勻。我們上面也講到了過了,hash 值的範圍值 2147483648到2147483647,前後加起來大概40億的對映空間,只要雜湊函式對映得比較均勻鬆散,一般應用是很難出現碰撞的。但問題是乙個40億長度的陣列,記憶體是放不下...