Java集合源碼分析之LinkedList

分析完了ListQueue之后,終于可以看看LinkedList的實(shí)現(xiàn)了。LinkedList彌補(bǔ)了ArrayList增刪較慢的問(wèn)題痛侍,但在查找方面又遜色于ArrayList钥弯,所以在使用時(shí)需要根據(jù)場(chǎng)景靈活選擇。對(duì)于這兩個(gè)頻繁使用的集合類垂寥,掌握它們的源碼并正確使用颠黎,可以讓我們的代碼更高效。

LinkedList既實(shí)現(xiàn)了List滞项,又實(shí)現(xiàn)了Deque狭归,前者使它能夠像使用ArrayList一樣使用,后者又使它能夠承擔(dān)隊(duì)列的職責(zé)文判。LinkedList內(nèi)部結(jié)構(gòu)是一個(gè)雙向鏈表过椎,我們?cè)诜治?code>ArrayDeque時(shí)提到過(guò)這個(gè)概念,就是擴(kuò)充單鏈表的指針域戏仓,增加一個(gè)指向前一個(gè)元素的指針previous疚宇。

AbstractSequentialList

AbstractSequentialListLinkedList的父級(jí),它繼承自AbstractList赏殃,并且是一個(gè)抽象類敷待,它主要為順序表的鏈?zhǔn)綄?shí)現(xiàn)提供一個(gè)骨架:

This class provides a skeletal implementation of the List interface to minimize the effort required to implement this interface backed by a "sequential access" data store (such as a linked list). For random access data (such as an array), AbstractList should be used in preference to this class.

意思是它的主要作用是提供一個(gè)實(shí)現(xiàn)List接口的骨架,來(lái)減少我們實(shí)現(xiàn)基于鏈?zhǔn)酱鎯?chǔ)的實(shí)現(xiàn)類時(shí)所需的工作量仁热。AbstractSequentialList并沒有做很多特殊的事情榜揖,其中最主要的是提供一個(gè)方法的默認(rèn)實(shí)現(xiàn),并將以下方法抽象抗蠢,以期有更符合場(chǎng)景的實(shí)現(xiàn):

public abstract ListIterator<E> listIterator(int index);

其他一些方法的實(shí)現(xiàn)都利用了這個(gè)listIterator方法举哟,我們不再一一查看了。下面我們分析LinkedList的實(shí)現(xiàn)

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

LinkedList的繼承結(jié)構(gòu)如下所示:

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

可以看到物蝙,LinkedList也實(shí)現(xiàn)了Cloneable炎滞、java.io.Serializable等方法,借鑒于ArrayList的經(jīng)驗(yàn)诬乞,我們可以想到它的Clone也是淺克隆册赛,在序列化方法也采用了同樣的方式钠导,我們就不再贅述了。

構(gòu)造方法與成員變量

數(shù)據(jù)單元Node

在介紹鏈表結(jié)構(gòu)時(shí)提到過(guò)森瘪,其數(shù)據(jù)單元分為數(shù)據(jù)域和指針域牡属,分別存儲(chǔ)數(shù)據(jù)和指向下一個(gè)元素的位置,在java中只要定義一個(gè)實(shí)體類就可以解決了扼睬。

private static class Node<E> {
    E item; //數(shù)據(jù)
    Node<E> next; //下一個(gè)元素
    Node<E> prev; //上一個(gè)元素

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

成員變量

LinkedList成員變量主要有三個(gè)逮栅,而且其意義清晰可見。

// 記錄當(dāng)前鏈表的長(zhǎng)度
transient int size = 0;

// 第一個(gè)節(jié)點(diǎn)
transient Node<E> first;

// 最后一個(gè)節(jié)點(diǎn)
transient Node<E> last;

構(gòu)造函數(shù)

因?yàn)殒湵頉]有長(zhǎng)度方面的問(wèn)題窗宇,所以也不會(huì)涉及到擴(kuò)容等問(wèn)題措伐,其構(gòu)造函數(shù)也十分簡(jiǎn)潔了。

public LinkedList() {
}

public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

一個(gè)默認(rèn)的構(gòu)造函數(shù)军俊,什么都沒有做侥加,一個(gè)是用其他集合初始化,調(diào)用了一下addAll方法粪躬。addAll方法我們就不再分析了担败,它應(yīng)該是和添加一個(gè)元素的方法是一致的。

重要方法

LinkedList既繼承了List镰官,又繼承了Deque提前,那它必然有一堆addremove泳唠、addFirst狈网、addLast等方法。這些方法的含義也相差不大警检,實(shí)現(xiàn)也是類似的孙援,因此LinkedList又提取了新的方法,來(lái)簡(jiǎn)化這些問(wèn)題扇雕。我們看看這些不對(duì)外的方法拓售,以及它們是如何與上述函數(shù)對(duì)應(yīng)的。

//將一個(gè)元素鏈接到首位
private void linkFirst(E e) {
    //先將原鏈表存起來(lái)
    final Node<E> f = first;
    //定義一個(gè)新節(jié)點(diǎn)镶奉,其next指向原來(lái)的first
    final Node<E> newNode = new Node<>(null, e, f);
    //將first指向新建的節(jié)點(diǎn)
    first = newNode;
    //原鏈表為空表
    if (f == null)
        //把last也指向新建的節(jié)點(diǎn)础淤,現(xiàn)在first與last都指向了它
        last = newNode;
    else
        //把原鏈表掛載在新建節(jié)點(diǎn),也就是現(xiàn)在的first之后
        f.prev = newNode;
    size++;
    modCount++;
}

//與linkFirst類似
void linkLast(E e) {
    //...
}

 //在某個(gè)非空節(jié)點(diǎn)之前添加元素
void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    //先把succ節(jié)點(diǎn)的前置節(jié)點(diǎn)存起來(lái)
    final Node<E> pred = succ.prev;
    //新節(jié)點(diǎn)插在pred與succ之間
    final Node<E> newNode = new Node<>(pred, e, succ);
    //succ的prev指針移到新節(jié)點(diǎn)
    succ.prev = newNode;
    //前置節(jié)點(diǎn)為空
    if (pred == null)
        //說(shuō)明插入到了首位
        first = newNode;
    else
        //把前置節(jié)點(diǎn)的next指針也指向新建的節(jié)點(diǎn)
        pred.next = newNode;
    size++;
    modCount++;
}

//刪除首位的元素哨苛,元素必須非空
private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    first = next;
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
}

private E unlinkLast(Node<E> l) {
    //...
}

//刪除一個(gè)指定的節(jié)點(diǎn)
E unlink(Node<E> x) {
    //...
}

可以看到鸽凶,LinkedList提供了一系列方法用來(lái)插入和刪除,但是卻沒有再實(shí)現(xiàn)一個(gè)方法來(lái)進(jìn)行查詢建峭,因?yàn)閷?duì)鏈表的查詢是比較慢的玻侥,所以它是通過(guò)另外的方法來(lái)實(shí)現(xiàn)的,我們看一下:

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

//可以說(shuō)盡力了
Node<E> node(int index) {
    // assert isElementIndex(index);
    
    //size>>1就是取一半的意思
    //折半亿蒸,將遍歷次數(shù)減少一半
    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;
    }
}

最后凑兰,我們看下它如何對(duì)應(yīng)那些繼承來(lái)的方法:

//引用了node方法掌桩,需要遍歷
public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}

//也可能需要遍歷
public void add(int index, E element) {
    checkPositionIndex(index);

    if (index == size)
            linkLast(element);
    else
        linkBefore(element, node(index));
}

//也要遍歷
public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}

public E peek() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

public E element() {
    return getFirst();
}

public E poll() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

public E remove() {
    return removeFirst();
}

public boolean offer(E e) {
    return add(e);
}

public boolean offerFirst(E e) {
    addFirst(e);
    return true;
}

//...

總結(jié)

LinkedList非常適合大量數(shù)據(jù)的插入與刪除,但其對(duì)處于中間位置的元素姑食,無(wú)論是增刪還是改查都需要折半遍歷波岛,這在數(shù)據(jù)量大時(shí)會(huì)十分影響性能。在使用時(shí)音半,盡量不要涉及查詢與在中間插入數(shù)據(jù)则拷,另外如果要遍歷,也最好使用foreach曹鸠,也就是Iterator提供的方式煌茬。

上一篇:Java集合源碼分析之Queue(三):ArrayDeque

下一篇:Java集合源碼分析之Map(一):超級(jí)接口Map


我是飛機(jī)醬,如果您喜歡我的文章物延,可以關(guān)注我~

編程之路宣旱,道阻且長(zhǎng)仅父。唯叛薯,路漫漫其修遠(yuǎn)兮,吾將上下而求索笙纤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耗溜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子省容,更是在濱河造成了極大的恐慌抖拴,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腥椒,死亡現(xiàn)場(chǎng)離奇詭異阿宅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)笼蛛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門洒放,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人滨砍,你說(shuō)我怎么就攤上這事往湿。” “怎么了惋戏?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵领追,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我响逢,道長(zhǎng)绒窑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任舔亭,我火速辦了婚禮些膨,結(jié)果婚禮上散罕,老公的妹妹穿的比我還像新娘。我一直安慰自己傀蓉,他們只是感情好欧漱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著葬燎,像睡著了一般误甚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谱净,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天窑邦,我揣著相機(jī)與錄音,去河邊找鬼壕探。 笑死冈钦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的李请。 我是一名探鬼主播瞧筛,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼导盅!你這毒婦竟也來(lái)了较幌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤白翻,失蹤者是張志新(化名)和其女友劉穎乍炉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滤馍,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岛琼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了巢株。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片槐瑞。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖纯续,靈堂內(nèi)的尸體忽然破棺而出随珠,到底是詐尸還是另有隱情,我是刑警寧澤猬错,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布窗看,位于F島的核電站,受9級(jí)特大地震影響倦炒,放射性物質(zhì)發(fā)生泄漏显沈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拉讯。 院中可真熱鬧涤浇,春花似錦、人聲如沸魔慷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)院尔。三九已至蜻展,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間邀摆,已是汗流浹背纵顾。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留栋盹,地道東北人施逾。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像例获,于是被迫代替她去往敵國(guó)和親汉额。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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

  • 財(cái)務(wù)發(fā)錢的時(shí)候,有一部分補(bǔ)貼件余,需要貼票,我以為具體數(shù)額會(huì)通知我遭居,結(jié)果沒人理 轉(zhuǎn)賬到帳號(hào)一筆服務(wù)費(fèi)用啼器,我以為轉(zhuǎn)賬完畢...
    廿九私房菜閱讀 356評(píng)論 0 0
  • 2016.12.06 微冷的一天 1 裝修已經(jīng)完全結(jié)束了,房子已經(jīng)完全成型啦俱萍。很滿意端壳,很滿足。 2雖然錯(cuò)過(guò)了班車枪蘑,...
    娑娑楊閱讀 244評(píng)論 0 0
  • “我還是很喜歡你损谦,像每天早起都要跑的五公里,風(fēng)雨不停岳颇≌占瘢”這是我聽過(guò)最美的告白。不知道這個(gè)兵哥對(duì)愛的堅(jiān)持是否已俘獲了...
    怪誕小妹閱讀 537評(píng)論 3 2
  • 人之初话侧,性本善栗精。 不知從什么時(shí)候開始,善良被人們所遺忘! 對(duì)于善良的我們剛步入這復(fù)雜的社會(huì)悲立,總是會(huì)有點(diǎn)小希望...
    X面具閱讀 200評(píng)論 2 3
  • “我已經(jīng)老了鹿寨,有一天,在一處公共場(chǎng)所的大廳里薪夕,有一個(gè)男人向我走來(lái)脚草。他主動(dòng)介紹自己,他對(duì)我說(shuō):‘我認(rèn)識(shí)你原献,永遠(yuǎn)記得你...
    Cain閱讀 719評(píng)論 0 3