HashMap 部分源碼解讀

前幾天在一個網(wǎng)站上回答問題,雖然是平時經(jīng)常遇到的匀归,但有很多點(diǎn)都被人模糊掉或者用一些專業(yè)名詞蓋掉坑资。筆者覺得這個問題很不錯,是一種學(xué)習(xí)思路穆端,于是回答后并將自己的答案記錄下來袱贮,如下:

1. if ((tab = table) == null || (n = tab.length) == 0)

2.? ? ? ? ? n = (tab = resize()).length;

3.? ? ? if ((p = tab[i = (n - 1) & hash]) == null)

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

5.? ? ? else {

6.? ? ? ? ? Node e; K k;

7.? ? ? ? ? if (p.hash == hash &&

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

9.? ? ? ? ? ? ? e = p;

10.? ? ? ? ? else if (p instanceof TreeNode)

11.? ? ? ? ? ? ? e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);

12.? ? ? ? else {

13.? ? ? ? ? ? for (int binCount = 0; ; ++binCount) {

14.? ? ? ? ? ? ? ? ? if ((e = p.next) == null) {

15.? ? ? ? ? ? ? ? ? ? p.next = newNode(hash, key, value, null);

16.? ? ? ? ? ? ? ? ? ? if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st

? 17.? ? ? ? ? ? ? ? ? ? ? ? ? treeifyBin(tab, hash);

? 18.? ? ? ? ? ? ? ? ? ? break;

? 19.? ? ? ? ? ? ? ? }

? 20.? ? ? ? ? ? ? ? ? if (e.hash == hash &&

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

? 22.? ? ? ? ? ? ? ? ? ? ? break;

? 23.? ? ? ? ? ? ? ? ? p = e;

? 24.? ? ? ? ? ? ? }

? 25.? ? ? ? }

解讀這一段源碼:

0. java1.8中,map的結(jié)構(gòu)是由Node[] + 鏈表組成的体啰,和之前的主要區(qū)別是攒巍,鏈表部分嗽仪,當(dāng)超過8個元素會轉(zhuǎn)成TreeNode對象,由TreeBin封裝柒莉,也就是鏈表轉(zhuǎn)換為樹形結(jié)構(gòu)便于存儲和查找闻坚。不是線程安全類,但是如果多線程操作會報(bào)出ConCurrentxxxException兢孝。

下面按行數(shù)來說:

1.? 如果當(dāng)前Node[] 為null 或者 Node數(shù)組元素個數(shù)為0 執(zhí)行下面一行代碼(省略大括號)

2.? resize() 方法返回一個擴(kuò)容后的Node[],擴(kuò)容的機(jī)制為所需容量的兩倍窿凤,是原有的1.5倍,這個計(jì)算是由負(fù)載因子 static final float DEFAULT_LOAD_FACTOR = 0.75f; 來決定的(具體的可以看一下resize()方法跨蟹,如果有困難我們再討論一次)雳殊。 Node數(shù)組元素個數(shù)n 賦值為擴(kuò)容后的長度。

4.? 「 (n - 1) & hash] 」定位數(shù)組下標(biāo)位置的經(jīng)典算法窗轩,你可以自己寫一個main方法來做幾個實(shí)驗(yàn)夯秃。n=16的話,計(jì)算結(jié)果肯定在0~15之間痢艺。p為計(jì)算后取到的一個Node節(jié)點(diǎn) 判斷是否為null (if else 省略大括號)

5.? 如果上述判斷為true仓洼,那么當(dāng)前節(jié)點(diǎn)賦值為一個新建節(jié)點(diǎn) newNode(x,x,x,x); 這個不用說吧。也是一個經(jīng)典數(shù)據(jù)結(jié)構(gòu)腹备。

6.? 如果4 中判斷結(jié)果為false衬潦,證明當(dāng)前下標(biāo)為i的位置存在節(jié)點(diǎn),那么進(jìn)行下列操作植酥。

7镀岛、8、9.? 如果當(dāng)前node節(jié)點(diǎn)的hash 和 節(jié)點(diǎn)中key屬性值 雙等或值等(也就是進(jìn)行新舊值替換)友驮,則將當(dāng)前節(jié)點(diǎn)賦值給e漂羊。

10、11.? 如果當(dāng)前節(jié)點(diǎn)為TreeNode節(jié)點(diǎn)卸留,則將當(dāng)前操作元素添加到TreeNode[]中(圖中走越,單獨(dú)的hash,key耻瑟,value都是當(dāng)前操作元素的值)旨指。

12.? 如果上述都不成立,直接執(zhí)行下列代碼(到這步的意思是喳整,不會是覆蓋操作谆构,當(dāng)前節(jié)點(diǎn)也不是treeNode,只能是將當(dāng)前節(jié)點(diǎn)拼接在列表末端)框都。

13-23. 這是一個看似無限循環(huán)的算法搬素,這有幾個退出點(diǎn),按順序來,第一個break熬尺,代表找到了列表末端摸屠,把當(dāng)前元素添加到末端即可。第二個break粱哼,是找到新舊值覆蓋的元素季二,進(jìn)行替換。

14行為常規(guī)列表遍歷皂吮。

15行為如果找到最后一個元素了戒傻,直接新建元素,將最后一個元素與新元素邏輯關(guān)聯(lián)蜂筹。

16行 判斷當(dāng)前個數(shù)需纳,是否大于或者等于7,如果是艺挪,則將列表轉(zhuǎn)化為TreeBin包裝類不翩,其實(shí)列表轉(zhuǎn)樹的界限是8,但是數(shù)組下標(biāo)是從0開始麻裳,所以是TREEIFY_THRESHOLD - 1口蝠。

17行就是轉(zhuǎn)化方法了。

補(bǔ)充 1:jdk8之后津坑,hashmap中妙蔗,數(shù)據(jù)結(jié)構(gòu)是Node[]+(鏈表|樹),Node[]肯定是不會改變的了疆瑰,那什么時候用鏈表眉反,什么時候是樹呢?他們有個臨界點(diǎn)穆役,8寸五,這個數(shù)字我在回答里也說過,你在源碼里也能找到耿币。知道這個8了梳杏,我們看下怎么轉(zhuǎn)化的,首先淹接,數(shù)組中的一個元素十性,他是從鏈表就夠開始構(gòu)造的,也就是說塑悼,你連續(xù)put相同的(lenght-1)&hash的值劲适,當(dāng)個數(shù)小于8的時候,用的都是鏈表拢肆,也是就是node.next=new Node()形式减响,當(dāng)個數(shù)大于等8的時候,將鏈表轉(zhuǎn)化為TreeNode形式(在內(nèi)部TreeNode托管給TreebBin郭怪,TreeNode是紅黑樹結(jié)構(gòu)支示。),p instenceof NodeTree 就是用來判斷鄙才,當(dāng)前數(shù)組元素結(jié)構(gòu)颂鸿,是鏈表還是樹,如果返回時true攒庵,那么嘴纺,當(dāng)前元素下的結(jié)構(gòu)已經(jīng)是樹,就用樹的方式將當(dāng)前操作的對象加入到樹中浓冒,反之還是鏈表栽渴,將當(dāng)前操作的對象,加入到鏈表末尾稳懒,加入完成之后闲擦,判斷是否需要轉(zhuǎn)化為樹結(jié)構(gòu),判斷條件是什么呢场梆,就是那個8墅冷。

補(bǔ)充 2: jdk8之后,初始化HashMap之后或油,他的容量size并不是16寞忿,初始化后的對象 是{},當(dāng)有寫操作進(jìn)行時,才會inittable 給他一個初始化容量1<<4,具體的請看源碼顶岸,如果不懂腔彰,歡迎留言一起討論。

補(bǔ)充 3: 有人說到底是看jdk7好還是8好蜕琴,我個人意見萍桌,以8為主,7也要看凌简,看他為什么這么優(yōu)化上炎,這么優(yōu)化的好處是什么,優(yōu)化的作者是如何想的雏搂,我想我們能把這種思想轉(zhuǎn)化為我們自己的藕施,jdk這種教科書也算是成功的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凸郑,一起剝皮案震驚了整個濱河市裳食,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芙沥,老刑警劉巖诲祸,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浊吏,死亡現(xiàn)場離奇詭異,居然都是意外死亡救氯,警方通過查閱死者的電腦和手機(jī)找田,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來着憨,“玉大人墩衙,你說我怎么就攤上這事〖锥叮” “怎么了漆改?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長准谚。 經(jīng)常有香客問我挫剑,道長,這世上最難降的妖魔是什么柱衔? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任暮顺,我火速辦了婚禮,結(jié)果婚禮上秀存,老公的妹妹穿的比我還像新娘捶码。我一直安慰自己,他們只是感情好或链,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布惫恼。 她就那樣靜靜地躺著,像睡著了一般澳盐。 火紅的嫁衣襯著肌膚如雪祈纯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天叼耙,我揣著相機(jī)與錄音腕窥,去河邊找鬼。 笑死筛婉,一個胖子當(dāng)著我的面吹牛簇爆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爽撒,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼入蛆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了硕勿?” 一聲冷哼從身側(cè)響起哨毁,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎源武,沒想到半個月后扼褪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體想幻,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年话浇,在試婚紗的時候發(fā)現(xiàn)自己被綠了举畸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡凳枝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跋核,到底是詐尸還是另有隱情岖瑰,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布砂代,位于F島的核電站蹋订,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏刻伊。R本人自食惡果不足惜露戒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捶箱。 院中可真熱鬧智什,春花似錦、人聲如沸丁屎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽晨川。三九已至证九,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間共虑,已是汗流浹背愧怜。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妈拌,地道東北人拥坛。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像尘分,于是被迫代替她去往敵國和親渴逻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

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