日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

hashmap 扩容是元素还是数组_曹工说JDK源码(1)--ConcurrentHashMap,扩容前大家同在一个哈希桶,为啥扩容后,你去新数组的高位,我只能去低位?...

發布時間:2024/8/23 编程问答 39 豆豆

如何計算,一對key/value應該放在哪個哈希桶

大家都知道,hashmap底層是數組+鏈表(不討論紅黑樹的情況),其中,這個數組,我們一般叫做哈希桶,大家如果去看jdk的源碼,會發現里面有一些變量,叫做bin,這個bin,就是桶的意思,結合語境,就是哈希桶。

這里舉個例子,假設一個hashmap的數組長度為4(0000 0100),那么該hashmap就有4個哈希桶,分別為bucket[0]、bucket[1]、bucket[2]、bucket[3]。

現在有兩個node,hashcode分別是1(0000 0001),5(0000 0101). 我們當然知道,這兩個node,都應該放入第一個桶,畢竟1 mod 4,5 mod 4的結果,都是1。

但是,在代碼里,可不是用取模的方法來計算的,而是使用下面的方式:

int entryNodeIndex = (tableLength - 1) & hash;

應該說,在tableLength的值,為2的n次冪的時候,兩者是等價的,但是因為位運算的效率更高,因此,代碼一般都使用位運算,替代取模運算。

下面我們看看具體怎么計算:

此處,tableLength即為哈希表的長度,此處為4. 4 - 1為3,3的二進制表示為:

0000 0011

那么,和我們的1(0000 0001)相與:

0000 0001 -------- 1

0000 0011 -------- 3(tableLength - 1)

相與(同為1,則為1;否則為0)

0000 0001 -------- 1

結果為1,所以,應該放在第1個哈希桶,即數組下標為1的node。

接下來,看看5這個hashcode的節點要放在什么位置,是怎么計算:

0000 0101 -------- 5

0000 0011 -------- 3(tableLength - 1)

相與(同為1,則為1;否則為0)后結果:

0000 0001 -------- 1

擴容時,是怎么對一個hash桶進行transfer的

此處,具體的整個transfer的細節,我們本講不會涉及太多,不過,大體的邏輯,我們可以來想一想。

以前面為例,哈希表一共4個桶,其中bucket[1]里面,存放了兩個元素,假設是a、b,其hashcode分別是1,5.

現在,假設我們要擴容,一般來說,擴容的時候,都是新建一個bucket數組,其容量為舊表的一倍,這里舊表為4,那新表就是8.

那,新表建立起來了,舊表里的元素,就得搬到新表里面去,等所有元素都搬到新表了,就會把新表和舊表的指針交換。如下:

java.util.concurrent.ConcurrentHashMap#transfer

private transient volatile Node[] nextTable;

transient volatile Node[] table;

if (finishing) {

// 1

nextTable = null;

// 2

table = nextTab;

// 3

sizeCtl = (tabLength << 1) - (tabLength >>> 1);

return;

}

1處,將field:nextTable(也就是新表)設為null,擴容完了,這個field就會設為null

2處,將局部變量nextTab,賦值給table,這個局部變量nextTab里,就是當前已經擴容完畢的新表

3處,修改表的sizeCtl為:假設此處tabLength為4,tabLength << 1 左移1位,就是8;tabLength >>> 1,右移一位,就是2,。8 - 2 = 6,正好就等于 8(新表容量) * 0.75。

所以,這里的sizeCtl就是,新表容量 * 負載因子,超過這個容量,基本就會觸發擴容。

ok,接著說,我們要怎么從舊表往新表搬呢? 那以前面的bucket[1]舉例,遍歷這個鏈表,計算各個node,應該放到新表的什么位置,不就完了嗎?是的,理論上這么寫就完事了。

但是,我們會怎么寫呢?

用hashcode對新bucket數組的長度取余嗎?

jdk對效率的追求那么高,肯定不會這么寫的,我們看看,它怎么寫的:

java.util.concurrent.ConcurrentHashMap#transfer

// 1

for (Node p = entryNode; p != null; p = p.next) {

// 2

int ph = p.hash;

K pk = p.key;

V pv = p.val;

// 3

if ((ph & tabLength) == 0){

lowEntryNode = new Node(ph, pk, pv, lowEntryNode);

}

else{

highEntryNode = new Node(ph, pk, pv, highEntryNode);

}

}

1處,即遍歷舊的哈希表的某個哈希桶,假設就是遍歷前面的bucket[1],里面有a/b兩個元素,hashcode分別為1,5那個。

2處,獲取該節點的hashcode,此處分別為1,5

3處,如果hashcode 和 舊表長度相與,結果為0,則,將該節點使用頭插法,插入新表的低位;如果結果不為0,則放入高位。

ok,什么是高位,什么是低位。擴容后,新的bucket數組,長度為8,那么,前面bucket[1]中的兩個元素,將分別放入bucket[1]和bucket[5].

ok,這里的bucket[1]就是低位,bucket[5]為高位。

首先,大家要知道,hashmap中,容量總是2的n次方,請牢牢記住這句話。

為什么要這么做?你想想,這樣是不是擴容很方便?

以前,hashcode 為1,5的,都在bucket[1];而現在,擴容為8后,hashcode為1的,還是在newbucket[1],hashcode為5的,則在newbucket[5];這樣的話,是不是有一半的元素,根本不用動?

這就是我覺得的,最大的好處;另外呢,運算也比較方便,都可以使用位運算代替,效率更高。

好的,那我們現在問題來了,下面這句的原理是什么?

if ((ph & tabLength) == 0){

lowEntryNode = new Node(ph, pk, pv, lowEntryNode);

} else{

highEntryNode = new Node(ph, pk, pv, highEntryNode);

}

為啥,hashcode & 舊哈希表的容量, 結果為0的,擴容后,就會在低位,也就是維持位置不變呢?而結果不為0的,擴容后,位置在高位呢?

背后的位運算原理(大白話)

代碼里用的如下判斷,滿足這個條件,去低位;否則,去高位。

if ((ph & tabLength) == 0)

還是用前面的例子,假設當前元素為a,hashcode為1,和哈希桶大小4,去進行與運算。

0000 0001 ---- 1

0000 0100 ---- 舊哈希表容量4

&運算(同為1則為1,否則為0)

結果:

0000 0000 ---- 結果為0

ok,這里算出來,結果為0;什么情況下,結果會為0呢?

那我們現在開始倒推,什么樣的數,和 0000 0100 相與,結果會為0?

???? ???? ----

0000 0100 ---- 舊哈希表容量

&運算(同為1則為1,否則為0)

結果:

0000 0000 ---- 結果為0

因為與運算的規則是,同為1,則為1;否則都為0。那么,我們這個例子里,舊哈希表容量為 0000 0100,假設表示為2的n次方,此處n為2,我們僅有第三位(第n+1)為1,那如果對方這一位為0,那結果中的這一位,就會為0,那么,整個數,就為0.

所以,我們的結論是:假設哈希表容量,為2的n次方,表示為二進制后,第n+1位為1;那么,只要我們節點的hashcode,在第n+1位上為0,則最終結果是0.

反之,如果我們節點的hashcode,在第n+1位為1,則最終結果不會是0.

比如,hashcode為5的時候,會是什么樣子?

0000 0101 ---- 5

0000 0100 ---- 舊哈希表容量

&運算(同為1則為1,否則為0)

結果:

0000 0100 ---- 結果為4

此時,5這個hashcode,在第n+1位上為1,所以結果不為0。

至此,我們離答案好像還很遠。ok,不慌,繼續。

假設現在擴容了,新bucket數組,長度為8.

a元素,hashcode依然是1,a元素應該放到新bucket數組的哪個bucket里呢?

我們用前面說的這個算法來計算:

int entryNodeIndex = (tableLength - 1) & hash;

0000 0001 ---- 1

0000 0111 ---- 8 - 1 = 7

&運算(同為1則為1,否則為0)

結果:

0000 0001 ---- 結果為1

結果沒錯,確實應該放到新bucket[1],但怎么推論出來呢?

// 1

if ((ph & tabLength) == 0){

// 2

lowEntryNode = new Node(ph, pk, pv, lowEntryNode);

}

也就是說,假設一個數,滿足1處的條件:(ph & tabLength) == 0,那怎么推論出2呢,即應該在低位呢?

ok,條件1,前面分析了,可以得出:

這個數,第n+1位為0.

接下來,看看數組長度 - 1這個數。

數組長度

2的n次方

二進制表示

1出現的位置

數組長度-1

數組長度-1的二進制

2

2的1次方

0000 0010

第2位

1

0000 0001

4

2的2次方

0000 0100

第3位

3

0000 0011

8

2的3次方

0000 1000

第4位

7

0000 0111

好了,兩個數都有了,

???????0??????? -- 1 節點的hashcode,第n + 1位為0

000000010000000 -- 2 老數組

000000100000000 -- 3 新數組的長度,等于老數組長度 * 2

000000011111111 -- 4 新數組的長度 - 1

運算:1和4相與

大家注意看紅字部分,還有框出來的那一列,這一列為0,導致,最終結果,肯定是比2那一行的數字小,2這行,不就是老數組的長度嗎,那你比老數組小;你比這一行小,在新數組里,就只能在低位了。

反之,如果節點的hashcode,這一位為1,那么,最終結果,至少是大于等于2這一行的數字,所以,會放在高位。

參考資料

原文:https://www.cnblogs.com/grey-wolf/p/13057567.html

總結

以上是生活随笔為你收集整理的hashmap 扩容是元素还是数组_曹工说JDK源码(1)--ConcurrentHashMap,扩容前大家同在一个哈希桶,为啥扩容后,你去新数组的高位,我只能去低位?...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: av电影在线播放 | 蜜桃视频一区二区三区 | 9999视频 | 亚洲少妇18p | 成人日韩精品 | 国产精品免费在线 | 欧美人与禽猛交乱配 | 欧美一级精品 | 91丨porny丨中文 | 欧洲黄视频 | 亚洲AV综合色区无码国产播放 | 久久奇米 | 黄色大片中文字幕 | 精品一区国产 | 日本视频免费在线播放 | 黄色a级片在线观看 | 亚洲天堂2021av | 成人国产精品入口免费视频 | 成人免费毛片糖心 | www.av在线免费观看 | 丰满饥渴老女人hd | 天天干精品 | 中文字幕免费高清视频 | 日本不卡一区二区在线观看 | 欧美日韩成人一区 | 中文字幕有码在线播放 | 日韩视频在线观看免费 | 亚洲欧美在线观看视频 | 国产精品久久久久久久久久妞妞 | 91麻豆国产在线 | 一本大道久久a久久综合婷婷 | 黄色一级片欧美 | 国产黄色大全 | 久久爱资源网 | 精品婷婷色一区二区三区蜜桃 | 不卡三区 | 2020国产精品视频 | 骚虎视频最新网址 | 校园伸入裙底揉捏1v1h | 色偷偷资源网 | 成人免费毛片高清视频 | 美女被男人桶出白浆喷水 | 香蕉一级视频 | av中文字幕免费在线观看 | 亚洲日本不卡 | 国产精品无码自拍 | 极品熟妇大蝴蝶20p 国产偷自拍视频 | 四虎精品在永久在线观看 | www日韩av | 日本乱偷人妻中文字幕在线 | 欧美作爱视频 | 美女的诞生免费观看在线高清 | 亚洲天堂一区二区三区 | 娇小萝被两个黑人用半米长 | 色噜噜狠狠一区二区三区果冻 | 亚洲国产黄色片 | 一区二区久久精品66国产精品 | 亚洲人成在线免费观看 | 久久国产精品波多野结衣av | 成人在线高清 | 波多野结衣av一区二区全免费观看 | 久久亚洲av永久无码精品 | 国产美女免费视频 | 午夜一区二区三区免费 | 国产视频你懂得 | 黑人糟蹋人妻hd中文字幕 | 免费看60分钟黄视频 | 韩国三级视频在线 | 亚洲免费a | 久久草精品 | 国产欧美视频一区二区 | 搡国产老太xxx网站 高h喷汁呻吟3p | 欧美日韩综合一区二区 | 少妇裸体挤奶汁奶水视频 | 一区二区三区视频免费看 | 俄罗斯毛片 | 韩国明星乱淫(高h)小说 | 久久不射网站 | 日韩av无码一区二区三区 | 潘金莲一级淫片免费放动漫 | 吃瓜网今日吃瓜 热门大瓜 色婷在线 | 丰满熟妇被猛烈进入高清片 | 清清草视频| 国产精品制服诱惑 | 亚洲av日韩av高潮潮喷无码 | 亚洲精品日本 | 亚洲乱码精品久久久久.. | www男人天堂| 丰满人妻翻云覆雨呻吟视频 | 四季av国产一区二区三区 | 男人午夜影院 | 国产野外作爱视频播放 | 欧美一级在线免费 | 日韩成人一级 | 国产精品丝袜黑色高跟鞋的设计特点 | 国产丰满农村老妇女乱 | 天天澡天天狠天天天做 | 久草在现| 欧美巨乳在线观看 |