源碼解析(JDK1.8)之——LinkedHashMap

1 LinkedHashMap

1.1 底層結(jié)構(gòu)

  1. LinkedHashMap可以認(rèn)為是HashMap(藍(lán)色部分)+LinkedList(黑色部分)瘦馍,即它既使用HashMap操作數(shù)據(jù)結(jié)構(gòu),又使用LinkedList維護(hù)插入元素的先后順序应役。

  2. LinkedHashMap的基本實(shí)現(xiàn)思想就是—-多態(tài)情组≡锟辏可以說(shuō),理解多態(tài)院崇,再去理解LinkedHashMap原理會(huì)事半功倍肆氓;反之也是,對(duì)于LinkedHashMap原理的學(xué)習(xí)底瓣,也可以促進(jìn)和加深對(duì)于多態(tài)的理解谢揪。

LinkedHashMap的數(shù)據(jù)結(jié)構(gòu)

說(shuō)明:LinkedHashMap會(huì)將元素串起來(lái),形成一個(gè)雙鏈表結(jié)構(gòu)捐凭〔Ψ觯可以看到,其結(jié)構(gòu)在HashMap結(jié)構(gòu)上增加了鏈表結(jié)構(gòu)茁肠。數(shù)據(jù)結(jié)構(gòu)為(數(shù)組 + 單鏈表 + 紅黑樹(shù) + 雙鏈表)患民,圖中的標(biāo)號(hào)是結(jié)點(diǎn)插入的順序。

1.2 優(yōu)缺點(diǎn)
有序的官套,即能夠保存元素插入的順序

2 四個(gè)關(guān)注點(diǎn)

關(guān)注點(diǎn) 結(jié)論
LinkedHashMap是否允許鍵值對(duì)為空 Key和Value都允許為空
LinkedHashMap是否允許重復(fù)數(shù)據(jù) Key重復(fù)會(huì)覆蓋酒奶,Value允許重復(fù)
LinkedHashMap是否有序 有序
LinkedHashMap是否線程安全 非線程安全

3 LinkedHashMap源碼解析

其實(shí),在分析了HashMap的源碼之后奶赔,我們來(lái)分析LinkedHashMap的源碼就會(huì)容易很多惋嚎,因?yàn)長(zhǎng)inkedHashMap是在HashMap基礎(chǔ)上進(jìn)行了擴(kuò)展,我們需要注意的就是兩者不同的地方站刑。

3.1 類的繼承關(guān)系

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

說(shuō)明:LinkedHashMap繼承了HashMap另伍,所以HashMap的一些方法或者屬性也會(huì)被繼承;同時(shí)也實(shí)現(xiàn)了Map結(jié)構(gòu)绞旅,關(guān)于HashMap類與Map接口摆尝,我們之前已經(jīng)分析過(guò),不再累贅因悲。

3.2 類的屬性

public class LinkedHashMap<K,V>  extends HashMap<K,V> implements Map<K,V> {
  
    // 版本序列號(hào)
    private static final long serialVersionUID = 3801124242820219131L;

    // 鏈表頭結(jié)點(diǎn)
    transient LinkedHashMap.Entry<K,V> head;

    // 鏈表尾結(jié)點(diǎn)
    transient LinkedHashMap.Entry<K,V> tail;

    // 訪問(wèn)順序
    final boolean accessOrder;
}

說(shuō)明:由于繼承HashMap堕汞,所以HashMap中的非private方法和字段,都可以在LinkedHashMap直接中訪問(wèn)晃琳。

3.3 類的構(gòu)造函數(shù)

1. LinkedHashMap(int, float)型構(gòu)造函數(shù)

public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
}

說(shuō)明:總是會(huì)在構(gòu)造函數(shù)的第一行調(diào)用父類構(gòu)造函數(shù)讯检,使用super關(guān)鍵字,accessOrder默認(rèn)為false卫旱,access為true表示之后訪問(wèn)順序按照元素的訪問(wèn)順序進(jìn)行人灼,即不按照之前的插入順序了,access為false表示按照插入順序訪問(wèn)顾翼。

2. LinkedHashMap(int)型構(gòu)造函數(shù)

public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
}

3. LinkedHashMap()型構(gòu)造函數(shù)

public LinkedHashMap() {
        super();
        accessOrder = false;
}

4. LinkedHashMap(Map<? extends K, ? extends V>)型構(gòu)造函數(shù)

public LinkedHashMap(Map<? extends K, ? extends V> m) {
    super();
    accessOrder = false;
    putMapEntries(m, false);
}

說(shuō)明:putMapEntries是調(diào)用到父類HashMap的函數(shù)

5. LinkedHashMap(int, float, boolean)型構(gòu)造函數(shù)

public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

說(shuō)明:可以指定accessOrder的值投放,從而控制訪問(wèn)順序。

3.4 類的重要函數(shù)分析

1. newNode函數(shù)

// 當(dāng)桶中結(jié)點(diǎn)類型為HashMap.Node類型時(shí)适贸,調(diào)用此函數(shù)
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
    // 生成Node結(jié)點(diǎn)
    LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e);
    // 將該結(jié)點(diǎn)插入雙鏈表末尾
    linkNodeLast(p);
    return p;
}

說(shuō)明:此函數(shù)在HashMap類中也有實(shí)現(xiàn)灸芳,LinkedHashMap重寫(xiě)了該函數(shù)涝桅,所以當(dāng)實(shí)際對(duì)象為L(zhǎng)inkedHashMap,桶中結(jié)點(diǎn)類型為Node時(shí)烙样,我們調(diào)用的是LinkedHashMap的newNode函數(shù)苹支,而非HashMap的函數(shù),newNode函數(shù)會(huì)在調(diào)用put函數(shù)時(shí)被調(diào)用误阻≌郏可以看到,除了新建一個(gè)結(jié)點(diǎn)之外究反,還把這個(gè)結(jié)點(diǎn)鏈接到雙鏈表的末尾了寻定,這個(gè)操作維護(hù)了插入順序。

其中LinkedHashMap.Entry繼承自HashMap.Node精耐。

static class Entry<K,V> extends HashMap.Node<K,V> {
    // 前后指針
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

說(shuō)明:在HashMap.Node基礎(chǔ)上增加了前后兩個(gè)指針域狼速,注意,HashMap.Node中的next域也存在卦停。

2. newTreeNode函數(shù)

// 當(dāng)桶中結(jié)點(diǎn)類型為HashMap.TreeNode時(shí)向胡,調(diào)用此函數(shù)
TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
    // 生成TreeNode結(jié)點(diǎn)
    TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
    // 將該結(jié)點(diǎn)插入雙鏈表末尾
    linkNodeLast(p);
    return p;
}

說(shuō)明:當(dāng)桶中結(jié)點(diǎn)類型為T(mén)reeNode時(shí)候,插入結(jié)點(diǎn)時(shí)調(diào)用的此函數(shù)惊完,也會(huì)鏈接到末尾僵芹。

3. afterNodeAccess函數(shù) 

void afterNodeAccess(Node<K,V> e) { // move node to last
    LinkedHashMap.Entry<K,V> last;
    // 若訪問(wèn)順序?yàn)閠rue,且訪問(wèn)的對(duì)象不是尾結(jié)點(diǎn)
    if (accessOrder && (last = tail) != e) {
        // 向下轉(zhuǎn)型小槐,記錄p的前后結(jié)點(diǎn)
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        // p的后結(jié)點(diǎn)為空
        p.after = null;
        // 如果p的前結(jié)點(diǎn)為空
        if (b == null)
            // a為頭結(jié)點(diǎn)
            head = a;
        else // p的前結(jié)點(diǎn)不為空
            // b的后結(jié)點(diǎn)為a
            b.after = a;
        // p的后結(jié)點(diǎn)不為空
        if (a != null)
            // a的前結(jié)點(diǎn)為b
            a.before = b;
        else // p的后結(jié)點(diǎn)為空
            // 后結(jié)點(diǎn)為最后一個(gè)結(jié)點(diǎn)
            last = b;
        // 若最后一個(gè)結(jié)點(diǎn)為空
        if (last == null)
            // 頭結(jié)點(diǎn)為p
            head = p;
        else { // p鏈入最后一個(gè)結(jié)點(diǎn)后面
            p.before = last;
            last.after = p;
        }
        // 尾結(jié)點(diǎn)為p
        tail = p;
        // 增加結(jié)構(gòu)性修改數(shù)量
        ++modCount;
    }
}

說(shuō)明:此函數(shù)在很多函數(shù)(如put)中都會(huì)被回調(diào)拇派,LinkedHashMap重寫(xiě)了HashMap中的此函數(shù)。若訪問(wèn)順序?yàn)閠rue凿跳,且訪問(wèn)的對(duì)象不是尾結(jié)點(diǎn)件豌,則下面的圖展示了訪問(wèn)前和訪問(wèn)后的狀態(tài),假設(shè)訪問(wèn)的結(jié)點(diǎn)為結(jié)點(diǎn)3

說(shuō)明:從圖中可以看到控嗜,結(jié)點(diǎn)3鏈接到了尾結(jié)點(diǎn)后面茧彤。

4. transferLinks函數(shù)

// 用dst替換src
private void transferLinks(LinkedHashMap.Entry<K,V> src,
                               LinkedHashMap.Entry<K,V> dst) {
    LinkedHashMap.Entry<K,V> b = dst.before = src.before;
    LinkedHashMap.Entry<K,V> a = dst.after = src.after;
    if (b == null)
        head = dst;
    else
        b.after = dst;
    if (a == null)
        tail = dst;
    else
        a.before = dst;
}

說(shuō)明:此函數(shù)用dst結(jié)點(diǎn)替換結(jié)點(diǎn),示意圖如下

說(shuō)明:其中只考慮了before與after域疆栏,并沒(méi)有考慮next域曾掂,next會(huì)在調(diào)用tranferLinks函數(shù)中進(jìn)行設(shè)定。

5. containsValue函數(shù)

public boolean containsValue(Object value) {
    // 使用雙鏈表結(jié)構(gòu)進(jìn)行遍歷查找
    for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
        V v = e.value;
        if (v == value || (value != null && value.equals(v)))
            return true;
    }
    return false;
}

說(shuō)明:containsValue函數(shù)根據(jù)雙鏈表結(jié)構(gòu)來(lái)查找是否包含value承边,是按照插入順序進(jìn)行查找的遭殉,與HashMap中的此函數(shù)查找方式不同石挂,HashMap是使用按照桶遍歷博助,沒(méi)有考慮插入順序。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末痹愚,一起剝皮案震驚了整個(gè)濱河市富岳,隨后出現(xiàn)的幾起案子蛔糯,更是在濱河造成了極大的恐慌,老刑警劉巖窖式,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚁飒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡萝喘,警方通過(guò)查閱死者的電腦和手機(jī)淮逻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)阁簸,“玉大人爬早,你說(shuō)我怎么就攤上這事∑裘茫” “怎么了筛严?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)饶米。 經(jīng)常有香客問(wèn)我桨啃,道長(zhǎng),這世上最難降的妖魔是什么檬输? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任照瘾,我火速辦了婚禮,結(jié)果婚禮上丧慈,老公的妹妹穿的比我還像新娘网杆。我一直安慰自己,他們只是感情好伊滋,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布碳却。 她就那樣靜靜地躺著,像睡著了一般笑旺。 火紅的嫁衣襯著肌膚如雪昼浦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,246評(píng)論 1 308
  • 那天筒主,我揣著相機(jī)與錄音关噪,去河邊找鬼。 笑死乌妙,一個(gè)胖子當(dāng)著我的面吹牛使兔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播藤韵,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼虐沥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起欲险,我...
    開(kāi)封第一講書(shū)人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤镐依,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后天试,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體槐壳,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年喜每,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了务唐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡带兜,死狀恐怖绍哎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鞋真,我是刑警寧澤崇堰,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站涩咖,受9級(jí)特大地震影響海诲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜檩互,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一特幔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧闸昨,春花似錦蚯斯、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至循诉,卻和暖如春横辆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背茄猫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工狈蚤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人划纽。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓脆侮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親勇劣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子靖避,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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