HashMap 源碼分析(二)

一、概述

在上一篇文章中陕习,我們分析了HashMap中增刪改查的流程,但這是遠(yuǎn)遠(yuǎn)不夠的址愿,所以在本文中该镣,我們將根據(jù)一些常見(jiàn)問(wèn)題并結(jié)合源碼來(lái)進(jìn)行更具體的分析。

二响谓、常見(jiàn)問(wèn)題

  1. HashMap的數(shù)組長(zhǎng)度為什么必須是2的冪损合?
  2. HashMap的默認(rèn)負(fù)載因子是多少,作用是什么娘纷?
  3. HashMap的默認(rèn)負(fù)載因子為什么是0.75嫁审?
  4. HashMax數(shù)組最大長(zhǎng)度是多少?
  5. 什么叫做Hash碰撞赖晶?
  6. HashMap何時(shí)擴(kuò)容律适?
  7. Jdk 1.8為什么加入了紅黑樹(shù)?
  8. 什么時(shí)候由鏈表轉(zhuǎn)為紅黑樹(shù)?

三擦耀、問(wèn)題解析

  1. HashMap的數(shù)組長(zhǎng)度為什么必須是2的冪棉圈?
    /**
     * The default initial capacity - MUST be a power of two.
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

通過(guò)上面的源碼注釋中,就已經(jīng)寫(xiě)明了:必須是 2 的冪眷蜓,這是為什么分瘾?源碼也不是我們寫(xiě)的,我們也不知道吁系,不過(guò)我們根據(jù)數(shù)組的長(zhǎng)度的使用來(lái)進(jìn)行分析德召,在HashMap的putVal方法中,有這樣一段代碼:

n = (tab = resize()).length;
p = tab[i = (n - 1) & hash]

這里的n為數(shù)組的長(zhǎng)度汽纤,p為計(jì)算出來(lái)的下標(biāo)上岗,下標(biāo)通過(guò)(16-1) & hash 進(jìn)行計(jì)算的。就是15&hash蕴坪,我們反向來(lái)思考問(wèn)題肴掷,如果數(shù)組長(zhǎng)度為15呢?我們來(lái)看下圖:


01.png

可以看到背传,當(dāng)數(shù)組長(zhǎng)度為15時(shí)呆瞻,通過(guò)&運(yùn)算,最終計(jì)算出來(lái)的下標(biāo)為:0径玖,2痴脾,4,6梳星,8赞赖,10,12冤灾,14前域,且均發(fā)生了哈希碰撞。那這時(shí)候數(shù)組的1 3 5 7 9 11 位置上不就存不上數(shù)據(jù)了嘛韵吨。

接下來(lái)我們?cè)倏串?dāng)數(shù)組長(zhǎng)度為2的冪次方時(shí)的結(jié)果:我們用2的3次方话侄,長(zhǎng)度為8舉例


02.png

可以看到,當(dāng)長(zhǎng)度為2的冪時(shí)学赛,最終算出的下標(biāo)是均勻分布的年堆,并沒(méi)有發(fā)生哈希碰撞。
細(xì)心的人可以發(fā)現(xiàn)盏浇,當(dāng)數(shù)組長(zhǎng)度不為2的冪時(shí)变丧,二進(jìn)制的最后一位總是為0,這就導(dǎo)致不管hash為多少绢掰,最終算出來(lái)總是雙數(shù)痒蓬,進(jìn)而導(dǎo)致了數(shù)組分布不均童擎,有些位置永遠(yuǎn)用不到。

所以:數(shù)組長(zhǎng)度為2的冪攻晒,是為了減少哈希碰撞顾复,是數(shù)組分布更均勻。



  1. HashMap的默認(rèn)負(fù)載因子是多少鲁捏,作用是什么芯砸?
static final float DEFAULT_LOAD_FACTOR = 0.75f;

newCap = DEFAULT_INITIAL_CAPACITY;
float ft = (float)newCap * loadFactor;

默認(rèn)的加載因子為0.75,它與HashMap的擴(kuò)容機(jī)制相關(guān)给梅,當(dāng)數(shù)組長(zhǎng)度大于16*0.75=12時(shí)假丧,數(shù)組就會(huì)擴(kuò)容,并不是到達(dá)16時(shí)才會(huì)擴(kuò)容动羽。它決定了HashMap擴(kuò)容的時(shí)機(jī)包帚。

  1. HashMap的默認(rèn)負(fù)載因子為什么是0.75?

上面提到了數(shù)組擴(kuò)容是由 長(zhǎng)度加載因子运吓,得到一個(gè)閾值渴邦,當(dāng)長(zhǎng)度達(dá)到這個(gè)閾值時(shí)就會(huì)擴(kuò)容,如果加載因子為1拘哨,那就是161,即數(shù)組長(zhǎng)度達(dá)到16時(shí)才會(huì)擴(kuò)容谋梭,但是這樣會(huì)犧牲時(shí)間效率,如果加載因子0.5時(shí)宅静,那么長(zhǎng)度為8時(shí)就擴(kuò)容了章蚣,此時(shí)就會(huì)導(dǎo)致數(shù)組中空間利用率降低站欺,所以0.75是一個(gè)比較合適的值姨夹。

  1. HashMax數(shù)組最大長(zhǎng)度是多少?
static final int MAXIMUM_CAPACITY = 1 << 30;

HashMap數(shù)組最大長(zhǎng)度為:2的30次冪矾策,1073741824

  1. 什么叫做Hash碰撞磷账?

在HashMap中,Hash碰撞就是指計(jì)算出的hash值相同贾虽。

  1. HashMap何時(shí)擴(kuò)容逃糟?
n = (tab = resize()).length; //629行

if (++size > threshold)
            resize();  //663行

在HashMap中,擴(kuò)容由resize()方法完成蓬豁,
在首次調(diào)用put時(shí)绰咽,會(huì)執(zhí)行resize()方法初始化,put之后判斷數(shù)量是否大于閾值地粪,如果大于閾值則擴(kuò)容取募,
后續(xù)調(diào)用會(huì)直接存入數(shù)據(jù),然后再判斷數(shù)量是否大于閾值蟆技,如果大于閾值則擴(kuò)容玩敏,
當(dāng)數(shù)組長(zhǎng)度大于加載因子*數(shù)組長(zhǎng)度時(shí)斗忌,則會(huì)擴(kuò)容。

  1. Jdk 1.8為什么加入了紅黑樹(shù)旺聚?

再Jdk1.7中织阳,HashMap是通過(guò)數(shù)組+鏈表實(shí)現(xiàn)的,Jdk1.8中加入了紅黑樹(shù)砰粹,只有當(dāng)數(shù)組中鏈表的長(zhǎng)度大于8時(shí)唧躲,才會(huì)轉(zhuǎn)為紅黑樹(shù),紅黑樹(shù)相比于鏈表在插入和刪除的操作上伸眶,要比鏈表更快惊窖,如果說(shuō)鏈表長(zhǎng)度為100時(shí),那么HashMap要循環(huán)99次才能找到最后一個(gè)節(jié)點(diǎn)厘贼,再執(zhí)行插入操作界酒,刪除同理。

  1. 什么時(shí)候由鏈表轉(zhuǎn)為紅黑樹(shù)嘴秸?
 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);


if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)  64
            resize();

當(dāng)鏈表的長(zhǎng)度大于8時(shí)執(zhí)行樹(shù)化的方法毁欣,隨后如果數(shù)組長(zhǎng)度小于64時(shí),則會(huì)擴(kuò)容岳掐,反之轉(zhuǎn)換為紅黑樹(shù)凭疮。
所以 當(dāng)鏈表長(zhǎng)度大于8時(shí) 且 數(shù)組長(zhǎng)度小于64時(shí)才會(huì)將鏈表轉(zhuǎn)為紅黑樹(shù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末串述,一起剝皮案震驚了整個(gè)濱河市执解,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纲酗,老刑警劉巖衰腌,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異觅赊,居然都是意外死亡右蕊,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)吮螺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饶囚,“玉大人,你說(shuō)我怎么就攤上這事鸠补÷芊纾” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵紫岩,是天一觀的道長(zhǎng)规惰。 經(jīng)常有香客問(wèn)我,道長(zhǎng)被因,這世上最難降的妖魔是什么卿拴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任衫仑,我火速辦了婚禮,結(jié)果婚禮上堕花,老公的妹妹穿的比我還像新娘文狱。我一直安慰自己,他們只是感情好缘挽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布瞄崇。 她就那樣靜靜地躺著,像睡著了一般壕曼。 火紅的嫁衣襯著肌膚如雪苏研。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天腮郊,我揣著相機(jī)與錄音摹蘑,去河邊找鬼。 笑死轧飞,一個(gè)胖子當(dāng)著我的面吹牛衅鹿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播过咬,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼大渤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了掸绞?” 一聲冷哼從身側(cè)響起泵三,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎衔掸,沒(méi)想到半個(gè)月后烫幕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡具篇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年纬霞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凌埂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驱显。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瞳抓,靈堂內(nèi)的尸體忽然破棺而出埃疫,到底是詐尸還是另有隱情,我是刑警寧澤孩哑,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布栓霜,位于F島的核電站,受9級(jí)特大地震影響横蜒,放射性物質(zhì)發(fā)生泄漏胳蛮。R本人自食惡果不足惜销凑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仅炊。 院中可真熱鬧斗幼,春花似錦、人聲如沸抚垄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)呆馁。三九已至桐经,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間浙滤,已是汗流浹背阴挣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纺腊,地道東北人屯吊。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像摹菠,于是被迫代替她去往敵國(guó)和親盒卸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

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