JDK HashMap詳解

HashMap的繼承關(guān)系如圖:


HashMap的繼承關(guān)系圖

初始空間大小 1<<4(16)
最大空間大小1 << 30 (1073741824)
哈希桶元素超過1杉允,鏈接方式默認(rèn)為鏈表形式民轴,數(shù)量超過TREEIFY_THRESHOLD的閾值(默認(rèn)8)將被轉(zhuǎn)換成紅黑樹
哈希桶的元素如果降低到UNTREEIFY_THRESHOLD的閾值(默認(rèn)6)會被重新轉(zhuǎn)換成鏈表形式
最小的哈希表容量為64(4倍TREEIFY_THRESHOLD值)
哈希表節(jié)點(diǎn)的類型為Node<K,V>(實(shí)現(xiàn)Map.Entry<K,V> 接口)

結(jié)構(gòu)為:
hash:哈希值
key:key
value:值
next:持有下一個節(jié)點(diǎn)的索引

put過程(以下是偽代碼,貼有部分jdk8的代碼實(shí)現(xiàn)):

  1. 計算key的哈希值
    采用哈希算法

     int h;
     (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
    

    這里也是哈希能兼容key為null的原因

    哈希算法為什么這樣寫?
    右位移16位闹啦,正好是32bit的一半,自己的高半?yún)^(qū)和低半?yún)^(qū)做異或勺拣,就是為了混合原始哈希碼的高位和低位竞思,以此來加大低位的隨機(jī)性。而且混合后的低位摻雜了高位的部分特征娘荡,這樣高位的信息也被變相保留下來。

  2. 將hash驶沼,key炮沐,value移交給putVal方法處理

  3. 處理節(jié)點(diǎn)數(shù)據(jù)

    1. 哈希表是空的:
      初始化哈希表,在初始化哈希表的過程中會進(jìn)行擴(kuò)容操作(resize()方法)會創(chuàng)建一個數(shù)組

      注意:實(shí)質(zhì)上初始化數(shù)組和數(shù)組容量達(dá)到當(dāng)前數(shù)組上限都會調(diào)用resize()方法并返回一個新的數(shù)組回怜,達(dá)到容量上限的時候大年,會將原有數(shù)組的值copy(遍歷到新的數(shù)組中)并返回新數(shù)組的引用,在擴(kuò)容的時候會將原有哈希表中的元素分散到新的哈希表中,因?yàn)闊o論如何計算哈西元素的時候當(dāng)前元素要么在當(dāng)前位置(j)要么在(j+oldCap)位置玉雾,形成新的哈希桶翔试,這樣可以快速確定元素位置,而不用去重新確定元素對于所有桶的位置复旬,這點(diǎn)很重要

    2. 根據(jù)當(dāng)前key的哈希和當(dāng)前的數(shù)組長度計算數(shù)值將被賦予的哈希桶位置垦缅,并賦值給如果當(dāng)前哈希桶首元素為null,則創(chuàng)建一個新的node并賦予當(dāng)前哈希表位置

    3. 如果不是null驹碍,則分為三種情況

      • map中已經(jīng)有當(dāng)前元素的key了.
        判定條件為:
        p.hash == hash &&
        ((k = p.key) == key || (key != null && key.equals(k)))
      • 當(dāng)前節(jié)點(diǎn)是一個TreeNode節(jié)點(diǎn)(超過了樹化閾值)
      • 沒有當(dāng)前元素并且不達(dá)到樹化條件,則添加元素到鏈表中并將引用存入node節(jié)點(diǎn)的next中,如果添加元素之后達(dá)到樹化條件壁涎,則將鏈表實(shí)現(xiàn)更新為紅黑樹的實(shí)現(xiàn)

      以下摘自網(wǎng)絡(luò)
      但是超過這個閾值后HashMap開始將列表升級成一個紅黑樹,使用哈希值作為樹的分支變量志秃,如果兩個哈希值不等怔球,但指向同一個桶的話,較大的那個會插入到右子樹里浮还。如果哈希值相等竟坛,HashMap希望key值最好是實(shí)現(xiàn)了Comparable接口的,這樣它可以按照順序來進(jìn)行插入碑定。這對HashMap的key來說并不是必須的流码,不過如果實(shí)現(xiàn)了當(dāng)然最好。如果沒有實(shí)現(xiàn)這個接口延刘,在出現(xiàn)嚴(yán)重的哈希碰撞的時候漫试,你就并別指望能獲得性能提升了。

    4. 如果map中存在次元素則替換元素的值并提供拓展點(diǎn)afterNodeAccess(Node<K,V> p)來處理這類數(shù)據(jù)

    5. 判定當(dāng)前容量已經(jīng)達(dá)到最大設(shè)置容量碘赖,如果達(dá)到給數(shù)組擴(kuò)容

    6. 提供拓展點(diǎn)給子類提供添加之后的一些處理afterNodeInsertion(boolean evict)

get過程(以下是偽代碼驾荣,貼有部分jdk8的代碼實(shí)現(xiàn)):

  1. 鏈表查找:計算key的哈希值,并獲取當(dāng)前哈希桶普泡,判定當(dāng)前哈希桶中的首元素是不是需要的數(shù)據(jù)(進(jìn)行equals)比較播掷,如果不是,則依次遍歷節(jié)點(diǎn)后驅(qū)直至找到key對應(yīng)的node

  2. 遍歷紅黑樹:判定首節(jié)點(diǎn)的類型是否是TreeNode撼班,如果是從紅黑樹中獲取元素并返回

  3. 如果沒有查找到元素則返回null

     需要注意的是:
     containsKey()和containsValue()方法歧匈,其實(shí)質(zhì)還是遍歷一遍哈希桶,所以砰嘁,如果只是判定在map中含有這個數(shù)據(jù)件炉,用這個方法當(dāng)然更好勘究,但是如果需要這個數(shù)據(jù)做下一步運(yùn)算就用get判空效率更高,因?yàn)閏ontainsKey()之后再次get相當(dāng)于調(diào)用了兩次遍歷斟冕,效率相對下降許多
    

keySet()和values()方法:

keySet()和values()是因?yàn)閔ashMap繼承自AbstractMap口糕,它提供了超類實(shí)現(xiàn)將node中的key和value拆分到兩個set中(keySet和values()),在第一次調(diào)用的時候(new KeySet的時候和values()的時候會創(chuàng)建一個對象磕蛇,這個對象引用了key序列和value序列景描,這里并不需要去維護(hù)這個數(shù)組,只需要實(shí)現(xiàn)了set的迭代器name這個對象就會被索引到set里面(很神奇秀撇,我也驚訝了好久 但是實(shí)驗(yàn)過后的確是這樣的))超棺,之后就能取得set里面的數(shù)據(jù),但是這個set的數(shù)值發(fā)生改變捌袜,map本身的數(shù)據(jù)也會發(fā)生改變说搅,里面存放的只是對象的key/value引用炸枣。

ps:沒有人比我寫的更詳細(xì)了吧虏等,就問還有誰還有誰!J食Α霍衫!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侯养,隨后出現(xiàn)的幾起案子敦跌,更是在濱河造成了極大的恐慌,老刑警劉巖逛揩,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柠傍,死亡現(xiàn)場離奇詭異,居然都是意外死亡辩稽,警方通過查閱死者的電腦和手機(jī)惧笛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逞泄,“玉大人患整,你說我怎么就攤上這事∨缰冢” “怎么了各谚?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長到千。 經(jīng)常有香客問我昌渤,道長,這世上最難降的妖魔是什么憔四? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任膀息,我火速辦了婚禮望抽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘履婉。我一直安慰自己煤篙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布毁腿。 她就那樣靜靜地躺著辑奈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪已烤。 梳的紋絲不亂的頭發(fā)上鸠窗,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天,我揣著相機(jī)與錄音胯究,去河邊找鬼稍计。 笑死,一個胖子當(dāng)著我的面吹牛裕循,可吹牛的內(nèi)容都是我干的臣嚣。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼剥哑,長吁一口氣:“原來是場噩夢啊……” “哼硅则!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起株婴,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤怎虫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后困介,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體大审,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年座哩,在試婚紗的時候發(fā)現(xiàn)自己被綠了徒扶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡八回,死狀恐怖酷愧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缠诅,我是刑警寧澤溶浴,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站管引,受9級特大地震影響士败,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一谅将、第九天 我趴在偏房一處隱蔽的房頂上張望漾狼。 院中可真熱鬧,春花似錦饥臂、人聲如沸逊躁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稽煤。三九已至,卻和暖如春囚戚,著一層夾襖步出監(jiān)牢的瞬間酵熙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工驰坊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匾二,地道東北人。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓拳芙,卻偏偏與公主長得像察藐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子态鳖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評論 2 361