日韩性视频-久久久蜜桃-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 | 精品国产99一区二区乱码综合 | 小柔的淫辱日记(1~7) | 一本大道一区二区 | 美日韩精品一区二区 | 性v天堂| 国产偷拍一区二区三区 | 久久一级免费视频 | 亚洲黄色在线播放 | 色资源在线观看 | 国产肉体xxxx裸体784大胆 | 亚洲精品水蜜桃 | 丰满少妇大力进入 | 欧美性猛交xxxx乱大交退制版 | 日本一区二区免费在线观看 | 欧美成人看片黄a免费看 | 亚洲av久久久噜噜噜熟女软件 | 亚洲色图欧美日韩 | 精品成人在线观看 | 五月天婷婷久久 | 在线观看 亚洲 | 中文字幕色网 | 免费人成视频在线 | 日韩欧美一区二区三区四区 | 久久免费在线观看视频 | av在线免费观看网站 | 伊人av在线播放 | 污污内射在线观看一区二区少妇 | 18禁裸男晨勃露j毛免费观看 | 亚洲成人网av | 一区二区国产视频 | 日韩一卡二卡三卡四卡 | 色网址在线观看 | 波多野结衣在线一区二区 | 国产激情视频一区二区三区 | 两个人做羞羞的视频 | 免费h片网站 | 中文无码精品一区二区三区 | 专干中国老太婆hd | 日本少妇高潮抽搐 | 国产精品久久毛片av大全日韩 | www黄色av| 自偷自拍av| 欧美人与性动交α欧美片 | 黄色大片日本 | 亚洲码无人客一区二区三区 | 国产成人自拍网 | 三级黄色网络 | 美女被男生免费视频 | 久久福利在线 | 秋霞av网 | 成人av动漫在线 | 亚洲永久在线观看 | 精品综合久久 | 国产精品99久 | 新av在线 | 久草热在线 | 四虎8848精品成人免费网站 | 97超碰人| 五月天精品视频 | 免费一级特黄特色毛片久久看 | 日韩在线免费视频观看 | 影音先锋久久久 | 久久久国产视频 | 在线观看国产福利 | 哺乳援交吃奶在线播放 | 九色免费视频 | 免费一级毛片麻豆精品 | 午夜国产在线观看 | 国产精彩视频在线 | 亚洲一区二区三区网站 | 色婷婷91 | av毛片网站 | 国产人成一区二区三区影院 | 欧美一级录像 | 国产精品视频在线观看免费 | www.日韩一区 | 中文字字幕第183页 欧美特级一级片 | 日少妇av| 亚洲激情啪啪 | 欧美aaaaaaa | 国产亚洲无码精品 | 成人国产片女人爽到高潮 | av中文字幕网址 | 欧美不卡在线观看 | 国产精品免费在线 | 91碰在线视频 | 污网站在线观看免费 | 国产亚洲精品成人无码精品网站 | 欧美 中文字幕 | 先锋影音制服丝袜 | 亚洲国产伊人 | 亚洲日本激情 | 国产97视频 | 在线成人免费电影 | 69视频免费 | 亚洲精品天堂在线 | 女女互慰吃奶互揉调教捆绑 | 久久国语对白 |