jdk1.8 LinkedList源碼全分析

本文原創(chuàng)地址,我的博客https://jsbintask.cn/2019/03/26/jdk/jdk8-linkedlist/(食用效果最佳)拱雏,轉(zhuǎn)載請(qǐng)注明出處!

前言

LinkedList內(nèi)部是一個(gè)鏈表的實(shí)現(xiàn)帘皿,一個(gè)節(jié)點(diǎn)除了保持自身的數(shù)據(jù)外晾匠,還持有前后专,后兩個(gè)節(jié)點(diǎn)的引用禁舷。所以就數(shù)據(jù)存儲(chǔ)上來(lái)說(shuō)拦键,它相比使用數(shù)組作為底層數(shù)據(jù)結(jié)構(gòu)的ArrayList來(lái)說(shuō)谣光,會(huì)更加耗費(fèi)空間。但也正因?yàn)檫@個(gè)特性芬为,它刪除萄金,插入節(jié)點(diǎn)很快!LinkedList沒(méi)有任何同步手段媚朦,所以多線程環(huán)境須慎重考慮氧敢,可以使用Collections.synchronizedList(new LinkedList(...));保證線程安全。

LinkedList

友情鏈接:jdk1.8 ArrayList源碼全分析

LinkedList結(jié)構(gòu)

類(lèi)關(guān)系

LinkedList

這里我們需要注意的是询张,相比于ArrayList孙乖,它額外實(shí)現(xiàn)了雙端隊(duì)列接口Deque,這個(gè)接口主要是聲明了隊(duì)頭份氧,隊(duì)尾的一系列方法唯袄。

類(lèi)成員

LinkedList

LinkedList內(nèi)部有兩個(gè)引用,一個(gè)first蜗帜,一個(gè)last恋拷,分別用于指向鏈表的頭和尾,另外有一個(gè)size厅缺,用于標(biāo)識(shí)這個(gè)鏈表的長(zhǎng)度蔬顾,而它的接的引用類(lèi)型是Node,這是他的一個(gè)內(nèi)部類(lèi):
LinkedList

很容易理解,item用于保存數(shù)據(jù)湘捎,而prve用于指向當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)诀豁,next用于指向當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)。

源碼解析

add(E e)方法

public boolean add(E e) {
    linkLast(e);
    return true;
}

這個(gè)方法直接調(diào)用linkLast:

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

我們用作圖來(lái)解釋下這個(gè)方法的執(zhí)行過(guò)程窥妇,一開(kāi)始舷胜,first和last都為null,此時(shí)鏈表什么都沒(méi)有活翩,當(dāng)?shù)谝淮握{(diào)用該方法后逞带,first和last均指向了第一個(gè)新加的節(jié)點(diǎn)E1:


LinkedList

接著,第二次調(diào)用該方法纱新,加入新節(jié)點(diǎn)E2。首先穆趴,將last引用賦值給l脸爱,接著new了一個(gè)新節(jié)點(diǎn)E2,并且E2的prve指向l未妹,接著將新節(jié)點(diǎn)E2賦值為last〔痉希現(xiàn)在結(jié)構(gòu)如下:


LinkedList

接著判斷l(xiāng)==null? 所以走的else語(yǔ)句空入,將l的next引用指向新節(jié)點(diǎn)E2,現(xiàn)在數(shù)據(jù)結(jié)構(gòu)如下:
LinkedList

接著size+1族檬,modCount+1歪赢,退出該方法,局部變量l銷(xiāo)毀单料,所以現(xiàn)在數(shù)據(jù)結(jié)構(gòu)如下:


LinkedList

這樣就完成了鏈表新節(jié)點(diǎn)的構(gòu)建埋凯。

add(int index, E element) 這個(gè)方法是在指定位置插入新元素

public void add(int index, E element) {
    checkPositionIndex(index);

    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}
  1. index位置檢查(不能小于0,大于size)
  2. 如果index==size扫尖,直接在鏈表最后插入白对,相當(dāng)于調(diào)用add(E e)方法
  3. 小于size,首先調(diào)用node方法將index位置的節(jié)點(diǎn)找出换怖,接著調(diào)用linkBefore
void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    final Node<E> pred = succ.prev;
    final Node<E> newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}

我們同樣作圖分析甩恼,假設(shè)現(xiàn)在鏈表中有三個(gè)節(jié)點(diǎn),調(diào)用node方法后找到的第二個(gè)節(jié)點(diǎn)E2沉颂,則進(jìn)入方法后条摸,結(jié)構(gòu)如下:


LinkedList

接著,將succ的prev賦值給pred铸屉,并且構(gòu)造新節(jié)點(diǎn)E4钉蒲,E4的prev和next分別為pred和suc,同時(shí)將新節(jié)點(diǎn)E4賦值為succ的prev引用抬探,則現(xiàn)在結(jié)構(gòu)如下:


LinkedList

接著子巾,將新節(jié)點(diǎn)賦值給pred節(jié)點(diǎn)的next引用,結(jié)構(gòu)如下:
LinkedList

最后小压,size+1线梗,modCount+1,推出方法怠益,本地變量succ仪搔,pred銷(xiāo)毀,最后結(jié)構(gòu)如下:


LinkedList

這樣新節(jié)點(diǎn)E4就插入在了第二個(gè)E2節(jié)點(diǎn)前面蜻牢。新鏈表構(gòu)建完成烤咧。從這個(gè)過(guò)程中我們可以知道,這里并沒(méi)有大量移動(dòng)移動(dòng)以前的元素抢呆,所以效率非常高煮嫌!

E get(int index)獲取指定節(jié)點(diǎn)數(shù)據(jù)

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

直接調(diào)用node方法:

Node<E> node(int index) {
    // assert isElementIndex(index);
    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}
  1. 判斷index在鏈表的哪邊。
  2. 遍歷查找index或者size-index次抱虐,找出對(duì)應(yīng)節(jié)點(diǎn)昌阿。
    這里我們知道,相比于數(shù)組的直接索引獲取,遍歷獲取節(jié)點(diǎn)效率并不高懦冰。

E remove(int index)移除指定節(jié)點(diǎn)

public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
  1. 檢查index位置
  2. 調(diào)用node方法獲取節(jié)點(diǎn)灶轰,接著調(diào)用unlink(E e)
E unlink(Node<E> x) {
    // assert x != null;
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }

    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }

    x.item = null;
    size--;
    modCount++;
    return element;
}

這個(gè)方法就不做分析了,其原理就是將當(dāng)前節(jié)點(diǎn)X的前一個(gè)節(jié)點(diǎn)P的next直接指向X的下一個(gè)節(jié)點(diǎn)D刷钢,這樣X(jué)就不再關(guān)聯(lián)任何引用笋颤,等待垃圾回收即可。
這里我們同樣知道内地,相比于ArrayList的copy數(shù)組覆蓋原來(lái)節(jié)點(diǎn)伴澄,效率同樣更高!

到現(xiàn)在瓤鼻,我們關(guān)于鏈表的核心方法秉版,增刪改都分析完畢,最后介紹下它實(shí)現(xiàn)的隊(duì)列Deque的各個(gè)方法:

LinkedList

  • add(E e):隊(duì)尾插入新節(jié)點(diǎn)茬祷,如果隊(duì)列空間不足清焕,拋出異常;LinkedList沒(méi)有空間限制祭犯,所以可以無(wú)限添加秸妥。
  • offer(E e):隊(duì)尾插入新節(jié)點(diǎn),空間不足沃粗,返回false粥惧,在LinkedList中和add方法同樣效果。
  • remove():移除隊(duì)頭節(jié)點(diǎn)最盅,如果隊(duì)列為空(沒(méi)有節(jié)點(diǎn)突雪,first為null),拋出異常涡贱。LinkedList中就是first節(jié)點(diǎn)(鏈表頭)
  • poll():同remove咏删,不同點(diǎn):隊(duì)列為空,返回null
  • element():查詢隊(duì)頭節(jié)點(diǎn)(不移除)问词,如果隊(duì)列為空督函,拋出異常。
  • peek():同element激挪,不同點(diǎn):隊(duì)列為空辰狡,返回null。

總結(jié)

  1. LinkedList內(nèi)部使用鏈表實(shí)現(xiàn)垄分,相比于ArrayList更加耗費(fèi)空間宛篇。
  2. LinkedList插入,刪除節(jié)點(diǎn)不用大量copy原來(lái)元素薄湿,效率更高叫倍。
  3. LinkedList查找元素使用遍歷豌鸡,效率一般。
  4. LinkedList同時(shí)是雙向隊(duì)列的實(shí)現(xiàn)段标。

關(guān)注我,這里只有干貨炉奴!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末逼庞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子瞻赶,更是在濱河造成了極大的恐慌赛糟,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砸逊,死亡現(xiàn)場(chǎng)離奇詭異璧南,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)师逸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)司倚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人篓像,你說(shuō)我怎么就攤上這事动知。” “怎么了员辩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵盒粮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我奠滑,道長(zhǎng)丹皱,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任宋税,我火速辦了婚禮摊崭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弃甥。我一直安慰自己爽室,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布淆攻。 她就那樣靜靜地躺著阔墩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瓶珊。 梳的紋絲不亂的頭發(fā)上啸箫,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音伞芹,去河邊找鬼忘苛。 笑死蝉娜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扎唾。 我是一名探鬼主播召川,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼胸遇!你這毒婦竟也來(lái)了荧呐?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤纸镊,失蹤者是張志新(化名)和其女友劉穎倍阐,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體逗威,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡峰搪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凯旭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片概耻。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖尽纽,靈堂內(nèi)的尸體忽然破棺而出咐蚯,到底是詐尸還是另有隱情,我是刑警寧澤弄贿,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布春锋,位于F島的核電站,受9級(jí)特大地震影響差凹,放射性物質(zhì)發(fā)生泄漏期奔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一危尿、第九天 我趴在偏房一處隱蔽的房頂上張望呐萌。 院中可真熱鬧,春花似錦谊娇、人聲如沸肺孤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)赠堵。三九已至,卻和暖如春法褥,著一層夾襖步出監(jiān)牢的瞬間茫叭,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工半等, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留揍愁,地道東北人呐萨。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像莽囤,于是被迫代替她去往敵國(guó)和親谬擦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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