HashMap源碼詳解 II ---- GET

上次寫(xiě)到了HashMap的put函數(shù)原理,這次我們來(lái)看一看HashMap在調(diào)用get函數(shù)的時(shí)候是什么原理.
上文已經(jīng)提到了HashMap的數(shù)據(jù)結(jié)構(gòu)是數(shù)組 + 鏈表/樹(shù) 的結(jié)構(gòu) 臀稚,那么我們要怎么樣才能根據(jù)key命中對(duì)應(yīng)的目標(biāo)呢售躁,且聽(tīng)分解.
強(qiáng)調(diào)一下 jdk版本為 1.8.0_121
以下為get(key)源碼

/**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise
     * it returns {@code null}.  (There can be at most one such mapping.)
     *
     * <p>A return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also
     * possible that the map explicitly maps the key to {@code null}.
     * The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     *
     * @see #put(Object, Object)
     */
    public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

get(Object key) 中,首先對(duì)key進(jìn)行hash處理,然后將hash值以及key傳入getNode(int hash, Object key),那么實(shí)際上就是由getNode函數(shù)去查詢對(duì)應(yīng)的Node/Entry。

接下來(lái)我們來(lái)分析getNode源碼:

    /**
     * Implements Map.get and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @return the node, or null if none
     */
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; 
        Node<K,V> first, e; 
        int n; 
        K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
//將table賦值到tab并判斷是否有值并且通過(guò)(n-1) & hash 計(jì)算出key所對(duì)應(yīng)的下標(biāo),找到該下標(biāo)的元素判斷其是否為null
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
//這里first元素便是所屬鏈表的入口元素 若first的hash以及k值與key的完全相同那么返回first          
                return first;
            if ((e = first.next) != null) {
  //這里獲取到first下一個(gè)元素 并判斷元素類型,若是TreeNode則使用平衡樹(shù)的查詢 若不是這遍歷鏈表剩余元素
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash && 
                        ((k = e.key) == key || (key != null && key.equals(k))))
//判斷他們的hash以及key值 返回滿足條件的元素
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

到這里,結(jié)合上一篇文章 HashMap PUT 詳解 首先要理解HashMap的數(shù)據(jù)結(jié)構(gòu),
結(jié)合數(shù)據(jù)結(jié)構(gòu)再去看源碼就十分容易理解了.
那么通過(guò)源碼的分析我們還能得出一些HashMap需要注意的點(diǎn):
1.盡量使用String和一些不可變類型作為key值.由源碼我們可以看出,會(huì)大量使用hash來(lái)進(jìn)行運(yùn)算,由于String 為final,對(duì)象不可變,因此hash值也會(huì)保證不變。
2.由于HashMap線程不安全,當(dāng)多個(gè)線程put時(shí)可能會(huì)造成線程數(shù)據(jù)丟失.

3.HashMap數(shù)據(jù)擴(kuò)容, 其實(shí)在上一篇中在put函數(shù)末尾有這么一句代碼

if (++size > threshold)
            resize();

當(dāng)數(shù)組大小大于閾值時(shí)會(huì)進(jìn)行擴(kuò)容,創(chuàng)建一個(gè)于當(dāng)前數(shù)據(jù)兩倍大小的數(shù)據(jù),并把舊數(shù)據(jù)放入新數(shù)組中碱茁。
4.在1.8之前resize的代碼跟1.8的區(qū)別比較大,建議大家對(duì)比一下。這里涉及到的是一個(gè)環(huán)形鏈表的問(wèn)題仿贬,在1.8之前的版本纽竣,當(dāng)兩個(gè)線程同時(shí)擴(kuò)容,會(huì)打亂鏈表順序有一定幾率形成環(huán)形鏈表導(dǎo)致無(wú)限循環(huán)茧泪。那么我們看看現(xiàn)在的resize代碼如下:

        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {//這里針對(duì)hash值對(duì)應(yīng)下標(biāo)沒(méi)有變化的元素
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) { 
//著重看這里 會(huì)將loTail的next設(shè)置為null 
//loTail 表示尾部元素
//這里每次循環(huán)鏈表完成組裝后便會(huì)設(shè)置尾部元素的next為null 避免了環(huán)形鏈表的產(chǎn)生
                            loTail.next = null; 
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;//這里同上理
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }

resize代碼比較長(zhǎng) 這里我只貼了比較重要的部分.
所以在1.8開(kāi)始 hashMap不會(huì)造成環(huán)形鏈表了哈蜓氨,大家注意了
本篇get 以及 resize就說(shuō)到這里了。
接下來(lái)的文章我準(zhǔn)備說(shuō)說(shuō)ConcurrentMap,給自己加油队伟!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載穴吹,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末嗜侮,一起剝皮案震驚了整個(gè)濱河市港令,隨后出現(xiàn)的幾起案子啥容,更是在濱河造成了極大的恐慌,老刑警劉巖顷霹,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咪惠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡泼返,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)姨拥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)绅喉,“玉大人,你說(shuō)我怎么就攤上這事叫乌〔窆蓿” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵憨奸,是天一觀的道長(zhǎng)革屠。 經(jīng)常有香客問(wèn)我,道長(zhǎng)排宰,這世上最難降的妖魔是什么似芝? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮板甘,結(jié)果婚禮上党瓮,老公的妹妹穿的比我還像新娘。我一直安慰自己盐类,他們只是感情好寞奸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著在跳,像睡著了一般枪萄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猫妙,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天瓷翻,我揣著相機(jī)與錄音,去河邊找鬼割坠。 笑死逻悠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的韭脊。 我是一名探鬼主播童谒,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼沪羔!你這毒婦竟也來(lái)了饥伊?” 一聲冷哼從身側(cè)響起象浑,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎琅豆,沒(méi)想到半個(gè)月后愉豺,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茫因,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年蚪拦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冻押。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驰贷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出洛巢,到底是詐尸還是另有隱情括袒,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布稿茉,位于F島的核電站锹锰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏漓库。R本人自食惡果不足惜恃慧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望渺蒿。 院中可真熱鬧糕伐,春花似錦、人聲如沸蘸嘶。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)训唱。三九已至褥蚯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間况增,已是汗流浹背赞庶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留澳骤,地道東北人歧强。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像为肮,于是被迫代替她去往敵國(guó)和親摊册。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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

  • 無(wú)處安放滴青春閱讀 154評(píng)論 0 0
  • 第一:形體很重要颊艳,精氣神很關(guān)鍵C┨亍忘分! 第二:演講的時(shí)候要融入感情于演講稿! 第三:表達(dá)要干凈白修!聲音要干凈6事汀!
    福添吉閱讀 182評(píng)論 0 0