HashMap 擴容方法

之前在高并發(fā)下 的HashMap遇到過的問題介返,第一個是元素多次插入HashMap達到一定飽和度時,隨著HashMap容量增加Key映射位置容易發(fā)生沖突,第二種就是在多線程下出現(xiàn)死循環(huán)圣蝎。擴容有兩步:創(chuàng)建一個新的Entry空數(shù)組刃宵,長度是原數(shù)組的2倍;遍歷原Entry數(shù)組徘公,把所有的Entry重新Hash到新數(shù)組组去。為什么要重新Hash呢?因為長度擴大以后步淹,Hash的規(guī)則也隨之改變。下面是擴容實現(xiàn)诚撵。在多線程下使用ConcurrentHashMap缭裆,ConcurrentHashMap是線程安全的。

1寿烟、final Node<K, V>[] resize() {

? ? // 舊數(shù)組

? ? Node<K, V>[] oldTab = table;

? ? // 舊容量

? ? int oldCap = (oldTab == null) ? 0 : oldTab.length;

? ? // 舊擴容門檻

? ? int oldThr = threshold;

? ? int newCap, newThr = 0;

? ? if (oldCap > 0) {

? ? ? ? if (oldCap >= MAXIMUM_CAPACITY) {

? ? ? ? ? ? // 如果舊容量達到了最大容量澈驼,則不再進行擴容

? ? ? ? ? ? threshold = Integer.MAX_VALUE;

? ? ? ? ? ? return oldTab;

? ? ? ? } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&

? ? ? ? ? ? ? ? oldCap >= DEFAULT_INITIAL_CAPACITY)

? ? ? ? ? ? // 如果舊容量的兩倍小于最大容量并且舊容量大于默認初始容量(16),則容量擴大為兩部筛武,擴容門檻也擴大為兩倍

? ? ? ? ? ? newThr = oldThr << 1; // double threshold

? ? } else if (oldThr > 0) // initial capacity was placed in threshold

? ? ? ? // 使用非默認構造方法創(chuàng)建的map缝其,第一次插入元素會走到這里

? ? ? ? // 如果舊容量為0且舊擴容門檻大于0,則把新容量賦值為舊門檻

? ? ? ? newCap = oldThr;

? ? else {? ? ? ? ? ? ? // zero initial threshold signifies using defaults

? ? ? ? // 調(diào)用默認構造方法創(chuàng)建的map徘六,第一次插入元素會走到這里

? ? ? ? // 如果舊容量舊擴容門檻都是0内边,說明還未初始化過,則初始化容量為默認容量待锈,擴容門檻為默認容量*默認裝載因子

? ? ? ? newCap = DEFAULT_INITIAL_CAPACITY;

? ? ? ? newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);

? ? }

? ? if (newThr == 0) {

? ? ? ? // 如果新擴容門檻為0漠其,則計算為容量*裝載因子,但不能超過最大容量

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

? ? ? ? newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ?

? ? ? ? ? ? ? ? (int) ft : Integer.MAX_VALUE);

? ? }

? ? // 賦值擴容門檻為新門檻

? ? threshold = newThr;

? ? // 新建一個新容量的數(shù)組

? ? @SuppressWarnings({"rawtypes", "unchecked"})

? ? Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap];

? ? // 把桶賦值為新數(shù)組

? ? table = newTab;

? ? // 如果舊數(shù)組不為空竿音,則搬移元素

? ? if (oldTab != null) {

? ? ? ? // 遍歷舊數(shù)組

? ? ? ? for (int j = 0; j < oldCap; ++j) {

? ? ? ? ? ? Node<K, V> e;

? ? ? ? ? ? // 如果桶中第一個元素不為空和屎,賦值給e

? ? ? ? ? ? if ((e = oldTab[j]) != null) {

? ? ? ? ? ? ? ? // 清空舊桶,便于GC回收?

? ? ? ? ? ? ? ? oldTab[j] = null;

? ? ? ? ? ? ? ? // 如果這個桶中只有一個元素春瞬,則計算它在新桶中的位置并把它搬移到新桶中

? ? ? ? ? ? ? ? // 因為每次都擴容兩倍柴信,所以這里的第一個元素搬移到新桶的時候新桶肯定還沒有元素

? ? ? ? ? ? ? ? if (e.next == null)

? ? ? ? ? ? ? ? ? ? newTab[e.hash & (newCap - 1)] = e;

? ? ? ? ? ? ? ? else if (e instanceof TreeNode)

? ? ? ? ? ? ? ? ? ? // 如果第一個元素是樹節(jié)點,則把這顆樹打散成兩顆樹插入到新桶中去

? ? ? ? ? ? ? ? ? ? ((TreeNode<K, V>) e).split(this, newTab, j, oldCap);

? ? ? ? ? ? ? ? else { // preserve order

? ? ? ? ? ? ? ? ? ? // 如果這個鏈表不止一個元素且不是一顆樹

? ? ? ? ? ? ? ? ? ? // 則分化成兩個鏈表插入到新的桶中去

? ? ? ? ? ? ? ? ? ? // 比如宽气,假如原來容量為4随常,3、7抹竹、11线罕、15這四個元素都在三號桶中

? ? ? ? ? ? ? ? ? ? // 現(xiàn)在擴容到8,則3和11還是在三號桶窃判,7和15要搬移到七號桶中去

? ? ? ? ? ? ? ? ? ? // 也就是分化成了兩個鏈表

? ? ? ? ? ? ? ? ? ? Node<K, V> loHead = null, loTail = null;

? ? ? ? ? ? ? ? ? ? Node<K, V> hiHead = null, hiTail = null;

? ? ? ? ? ? ? ? ? ? Node<K, V> next;

? ? ? ? ? ? ? ? ? ? do {

? ? ? ? ? ? ? ? ? ? ? ? next = e.next;

? ? ? ? ? ? ? ? ? ? ? ? // (e.hash & oldCap) == 0的元素放在低位鏈表中

? ? ? ? ? ? ? ? ? ? ? ? // 比如钞楼,3 & 4 == 0

? ? ? ? ? ? ? ? ? ? ? ? if ((e.hash & oldCap) == 0) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? if (loTail == null)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? loHead = e;

? ? ? ? ? ? ? ? ? ? ? ? ? ? else

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? loTail.next = e;

? ? ? ? ? ? ? ? ? ? ? ? ? ? loTail = e;

? ? ? ? ? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? ? ? ? ? // (e.hash & oldCap) != 0的元素放在高位鏈表中

? ? ? ? ? ? ? ? ? ? ? ? ? ? // 比如,7 & 4 != 0

? ? ? ? ? ? ? ? ? ? ? ? ? ? if (hiTail == null)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hiHead = e;

? ? ? ? ? ? ? ? ? ? ? ? ? ? else

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hiTail.next = e;

? ? ? ? ? ? ? ? ? ? ? ? ? ? hiTail = e;

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? } while ((e = next) != null);

? ? ? ? ? ? ? ? ? ? // 遍歷完成分化成兩個鏈表了

? ? ? ? ? ? ? ? ? ? // 低位鏈表在新桶中的位置與舊桶一樣(即3和11還在三號桶中)

? ? ? ? ? ? ? ? ? ? if (loTail != null) {

? ? ? ? ? ? ? ? ? ? ? ? loTail.next = null;

? ? ? ? ? ? ? ? ? ? ? ? newTab[j] = loHead;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? // 高位鏈表在新桶中的位置正好是原來的位置加上舊容量(即7和15搬移到七號桶了)

? ? ? ? ? ? ? ? ? ? if (hiTail != null) {

? ? ? ? ? ? ? ? ? ? ? ? hiTail.next = null;

? ? ? ? ? ? ? ? ? ? ? ? newTab[j + oldCap] = hiHead;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? return newTab;

}


1袄琳、如果使用是默認構造方法询件,則第一次插入元素時初始化為默認值燃乍,容量為16,擴容門檻為12宛琅;

2刻蟹、如果使用的是非默認構造方法,則第一次插入元素時初始化容量等于擴容門檻嘿辟,擴容門檻在構造方法里等于傳入容量向上最近的2的n次方舆瘪;

3、如果舊容量大于0红伦,則新容量等于舊容量的2倍英古,但不超過最大容量2的30次方,新擴容門檻為舊擴容門檻的2倍昙读;

4召调、創(chuàng)建一個新容量的桶;

5蛮浑、搬移元素唠叛,原鏈表分化成兩個鏈表,低位鏈表存儲在原來桶的位置沮稚,高位鏈表搬移到原來桶的位置加舊容量的位置艺沼;

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蕴掏,隨后出現(xiàn)的幾起案子澳厢,更是在濱河造成了極大的恐慌,老刑警劉巖囚似,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剩拢,死亡現(xiàn)場離奇詭異,居然都是意外死亡饶唤,警方通過查閱死者的電腦和手機徐伐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來募狂,“玉大人办素,你說我怎么就攤上這事』銮睿” “怎么了性穿?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長雷滚。 經(jīng)常有香客問我需曾,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任呆万,我火速辦了婚禮商源,結果婚禮上,老公的妹妹穿的比我還像新娘谋减。我一直安慰自己牡彻,他們只是感情好,可當我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布出爹。 她就那樣靜靜地躺著庄吼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪严就。 梳的紋絲不亂的頭發(fā)上霸褒,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天,我揣著相機與錄音盈蛮,去河邊找鬼。 笑死技矮,一個胖子當著我的面吹牛抖誉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播衰倦,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼袒炉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了樊零?” 一聲冷哼從身側響起我磁,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驻襟,沒想到半個月后夺艰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡沉衣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年郁副,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豌习。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡存谎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肥隆,到底是詐尸還是另有隱情既荚,我是刑警寧澤,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布栋艳,位于F島的核電站恰聘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜憨琳,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一诫钓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧篙螟,春花似錦菌湃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绪杏,卻和暖如春下愈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蕾久。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工势似, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人僧著。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓履因,卻偏偏與公主長得像,于是被迫代替她去往敵國和親盹愚。 傳聞我的和親對象是個殘疾皇子栅迄,可洞房花燭夜當晚...
    茶點故事閱讀 45,781評論 2 361

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