HashMap底層實(shí)現(xiàn)原理(下)

上一篇文章我們介紹了HashMap的底層實(shí)現(xiàn),但還遺留了一點(diǎn)內(nèi)容,我們?cè)倩仡櫼幌律弦黄恼吕镎f(shuō)的內(nèi)容

執(zhí)行完紅框里的代碼景醇,personMap里放入了8個(gè)元素,放置完成后在堆內(nèi)存表現(xiàn)如下圖

如果忽略底層實(shí)現(xiàn)細(xì)節(jié)吝岭,是這樣的

在Map中三痰,一個(gè)key,對(duì)應(yīng)了一個(gè)value窜管,如果key的值已經(jīng)存在散劫,Map會(huì)直接替換value的內(nèi)容,來(lái)看一下源碼中是怎么實(shí)現(xiàn)的幕帆,來(lái)看以下代碼

Person oldPerson1 = personMap.put("張三", new Person("新張三", 21));

Person oldPerson2 = personMap.put("孫七", new Person("新孫七", 32));

System.out.println("oldPerson1.getName() :" + oldPerson1.getName());

System.out.println("oldPerson2.getName() : " + oldPerson2.getName());

System.out.println("personMap.size() : " + personMap.size());

new了一個(gè)Person“新張三”获搏,注意,key依然是張三失乾,看一下源碼

放入“新張三”時(shí)常熙,會(huì)執(zhí)行以上代碼1、2碱茁、5

iif ((p = tab[i = (n - 1) & hash]) == null)

? ? tab[i] = newNode(hash, key, value, null);

上面這段代碼在上一篇文章已經(jīng)改寫(xiě)過(guò)了裸卫,改寫(xiě)后的代碼如下:

i = (n - 1) & hash;//hash是傳過(guò)來(lái)的,其中n是底層數(shù)組的長(zhǎng)度纽竣,用&運(yùn)算符計(jì)算出i的值

p = tab[i];//用計(jì)算出來(lái)的i的值作為下標(biāo)從數(shù)組中元素

if(p == null){//這兒P不為null墓贿,所以下面這行代碼不會(huì)執(zhí)行茧泪。

? ? tab[i] = newNode(hash, key, value, null);//這行代碼不會(huì)執(zhí)行

}

很簡(jiǎn)單,直接在底層數(shù)組里取值賦值給p聋袋,由于p不為null队伟,執(zhí)行else里的邏輯

Node<K,V> e; K k;

if (p.hash == hash &&? //如果hash值相等,key也相等幽勒,或者equals相等缰泡,賦值給e

? ? ((k = p.key) == key || (key != null && key.equals(k))))

? ? ? e = p;//賦值給e

又看到了熟悉的equals方法,這里我們hash值相等代嗤,key的值也相等棘钞,條件成立,把值賦值給e干毅。(如果key的值不相等宜猜,就比較equals方法,也就是說(shuō)硝逢,就算key是一個(gè)新new出來(lái)的對(duì)象姨拥,只要滿(mǎn)足equals,也視為key相同

if (e != null) { // existing mapping for key

? ? V oldValue = e.value;//定義一個(gè)變量來(lái)存舊值

? ? if (!onlyIfAbsent || oldValue == null)

? ? e.value = value;//把value的值賦值為新的值

? ? afterNodeAccess(e);

? ? return oldValue;//返回的值

}

這段代碼就比較簡(jiǎn)單了渠鸽,用新的value替換舊value并返回舊的value叫乌。畫(huà)一下圖

再new一個(gè)Person“新孫七”并put到personMap中,注意徽缚,key依然是“孫七”憨奸,會(huì)執(zhí)行圖17-2里的1、2凿试、3排宰、4、5那婉,由于2板甘、3不滿(mǎn)足條件,實(shí)際執(zhí)行的是1详炬、4盐类、5,1這一步已經(jīng)說(shuō)過(guò)了呛谜,重點(diǎn)說(shuō)一下4這一步

for (int binCount = 0; ; ++binCount) {//循環(huán)

? ? if ((e = p.next) == null) {//如果循環(huán)到最后也沒(méi)找到在跳,把元素放到最后

? ? ? ? p.next = newNode(hash, key, value, null);//把元素放到最后

? ? ? ? if (binCount >= TREEIFY_THRESHOLD - 1) //如果長(zhǎng)度超>=8,轉(zhuǎn)換成紅黑樹(shù)

? ? ? ? ? ? treeifyBin(tab, hash);//轉(zhuǎn)換成紅黑樹(shù)

? ? ? ? ? ? break;

? ? ? ? }

? ? ? ? if (e.hash == hash && //這段代碼和第2步一樣

? ? ? ? ? ? ((k = e.key) == key || (key != null && key.equals(k))))

? ? ? ? ? ? break;

? ? ? ? ? ? p = e;//如果hash值相等呻率,key也相等或者equals相等硬毕,賦值給e

? ? ? ? }

? ? }

}

其實(shí)就是循環(huán)鏈表的節(jié)點(diǎn),直到找到"孫七"這個(gè)key礼仗,然后執(zhí)行圖17-2里的第5步吐咳,如果找不到逻悠,就添加到最后,這里我們key是“孫七”韭脊,在鏈表中找到元素替換value即可童谒,再畫(huà)一下圖

最后來(lái)看看放到樹(shù)里的方法putTreeVal,由于樹(shù)的內(nèi)容我們還沒(méi)涉及到沪羔,下面只標(biāo)注出了關(guān)鍵代碼

和鏈表類(lèi)似饥伊,循環(huán)(遍歷)樹(shù)的節(jié)點(diǎn),如果找到節(jié)點(diǎn)蔫饰,返回節(jié)點(diǎn)琅豆,執(zhí)行圖17-2里的第5步更新value。如果循環(huán)完整顆數(shù)都找不到相應(yīng)的key篓吁,添加新節(jié)點(diǎn)茫因。

最后我們看一下本文初那段示例代碼的執(zhí)行結(jié)果:


雖然元素已經(jīng)替換成新的值,但示例中打印的是替換前的值杖剪,元素個(gè)數(shù)還是8不變冻押,debug看一下,是不是value更新成功了

更新已經(jīng)成功盛嘿。

結(jié)合上一篇內(nèi)容洛巢,做一個(gè)總結(jié),在hashMap中放入(put)元素次兆,有以下重要步驟:

1稿茉、計(jì)算key的hash值,算出元素在底層數(shù)組中的下標(biāo)位置类垦。

2狈邑、通過(guò)下標(biāo)位置定位到底層數(shù)組里的元素(也有可能是鏈表也有可能是樹(shù))城须。

3蚤认、取到元素,判斷放入元素的key是否==或equals當(dāng)前位置的key糕伐,成立則替換value值砰琢,返回舊值。

4良瞧、如果是樹(shù)陪汽,循環(huán)樹(shù)中的節(jié)點(diǎn),判斷放入元素的key是否==或equals節(jié)點(diǎn)的key褥蚯,成立則替換樹(shù)里的value挚冤,并返回舊值,不成立就添加到樹(shù)里赞庶。

5训挡、否則就順著元素的鏈表結(jié)構(gòu)循環(huán)節(jié)點(diǎn)澳骤,判斷放入元素的key是否==或equals節(jié)點(diǎn)的key,成立則替換鏈表里value澜薄,并返回舊值为肮,找不到就添加到鏈表的最后。

精簡(jiǎn)一下肤京,判斷放入HashMap中的元素要不要替換當(dāng)前節(jié)點(diǎn)的元素颊艳,key滿(mǎn)足以下兩個(gè)條件即可替換:

1、hash值相等忘分。

2棋枕、==或equals的結(jié)果為true。

由于hash算法依賴(lài)于對(duì)象本身的hashCode方法妒峦,所以對(duì)于HashMap里的元素來(lái)說(shuō)戒悠,hashCode方法與equals方法非常的重要,這也是在說(shuō)說(shuō)Java里的equals(中)一文中強(qiáng)調(diào)重寫(xiě)對(duì)象的equals方法一定要重寫(xiě)hashCode方法的原因舟山,不重寫(xiě)的話绸狐,放到HashMap中可能會(huì)得不到你想要的結(jié)果!本示例中放入的key是String類(lèi)型的累盗,String這個(gè)類(lèi)已經(jīng)重寫(xiě)了hashCode方法寒矿,有興趣的朋友可以自行查看源碼。

轉(zhuǎn)載出自:https://zhuanlan.zhihu.com/p/28587782

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末若债,一起剝皮案震驚了整個(gè)濱河市符相,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蠢琳,老刑警劉巖啊终,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異傲须,居然都是意外死亡蓝牲,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)泰讽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)例衍,“玉大人,你說(shuō)我怎么就攤上這事已卸》鹦” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵累澡,是天一觀的道長(zhǎng)梦抢。 經(jīng)常有香客問(wèn)我,道長(zhǎng)愧哟,這世上最難降的妖魔是什么奥吩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任具伍,我火速辦了婚禮,結(jié)果婚禮上圈驼,老公的妹妹穿的比我還像新娘人芽。我一直安慰自己,他們只是感情好绩脆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布萤厅。 她就那樣靜靜地躺著,像睡著了一般靴迫。 火紅的嫁衣襯著肌膚如雪惕味。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天玉锌,我揣著相機(jī)與錄音名挥,去河邊找鬼。 笑死主守,一個(gè)胖子當(dāng)著我的面吹牛禀倔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播参淫,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼救湖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了涎才?” 一聲冷哼從身側(cè)響起鞋既,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎耍铜,沒(méi)想到半個(gè)月后邑闺,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棕兼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年陡舅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片程储。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蹭沛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出章鲤,到底是詐尸還是另有隱情,我是刑警寧澤咆贬,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布败徊,位于F島的核電站,受9級(jí)特大地震影響掏缎,放射性物質(zhì)發(fā)生泄漏皱蹦。R本人自食惡果不足惜煤杀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沪哺。 院中可真熱鬧沈自,春花似錦、人聲如沸辜妓。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)籍滴。三九已至酪夷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間孽惰,已是汗流浹背晚岭。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留勋功,地道東北人坦报。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像狂鞋,于是被迫代替她去往敵國(guó)和親燎竖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • HashMap 簡(jiǎn)介 HashMap 主要用來(lái)存放鍵值對(duì)要销,它基于哈希表的Map接口實(shí)現(xiàn)构回,是常用的Java集合之一。...
    Java面試指南閱讀 756評(píng)論 0 0
  • 本系列出于AWeiLoveAndroid的分享疏咐,在此感謝纤掸,再結(jié)合自身經(jīng)驗(yàn)查漏補(bǔ)缺,完善答案浑塞。以成系統(tǒng)借跪。 Java基...
    濟(jì)公大將閱讀 1,528評(píng)論 1 6
  • HashMap簡(jiǎn)介HashMap基于哈希表的 Map 接口的實(shí)現(xiàn)。此實(shí)現(xiàn)提供所有可選的映射操作酌壕,并允許使用 nul...
    小小的coder閱讀 244評(píng)論 0 0
  • 鑒于最近老被人說(shuō)不好好學(xué)習(xí)前端掏愁,我就重復(fù)給自己洗腦,壓制自己卵牍,準(zhǔn)備慢慢走向正軌果港。前幾天一時(shí)學(xué)習(xí)心起準(zhǔn)備記錄下...
    懶懶加1閱讀 182評(píng)論 3 0
  • 公司現(xiàn)在進(jìn)行一個(gè)大項(xiàng)目Muster Fundamental旨在打造標(biāo)準(zhǔn)化、一流化的營(yíng)運(yùn)流程糊昙,提供獨(dú)一無(wú)二的顧客體驗(yàn)...
    亞南成長(zhǎng)錄閱讀 48評(píng)論 0 0