LinkedList源碼分析

LinkedList是Java中常用的一個集合類筐喳,是List接口的一個實現(xiàn)類铜幽,而List接口繼承自Collection接口吁讨,所以LinkedList是Collection的一個實現(xiàn)類届巩。

本篇主要討論一下LinkedList底層代碼的實現(xiàn)壮池。

核心成員變量

    //size表示linkedList的大小偏瓤,即該linkedList已經(jīng)存儲了多少個元素
    transient int size = 0;
    
    //由于linkedList是由鏈表實現(xiàn)的,該first表示鏈表的第一個節(jié)點(diǎn)
    transient Node<E> first;
    
    //由于linkedList是由鏈表實現(xiàn)的椰憋,該list表示鏈表的最后個節(jié)點(diǎn)
    transient Node<E> last;

Node的實現(xiàn)

   //該Node是由雙鏈表的實現(xiàn)
   private static class Node<E> {
        //該節(jié)點(diǎn)表示實際存儲的元素
        E item;
        //該節(jié)點(diǎn)的下一個節(jié)點(diǎn)
        Node<E> next;
        //該節(jié)點(diǎn)的上一個節(jié)點(diǎn)
        Node<E> prev;

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

LinkedList無參構(gòu)造函數(shù)

   //該構(gòu)造函數(shù)實際什么也沒有做
    public LinkedList() {
    }

add(E e)方法的實現(xiàn)

   //該方法向鏈表尾部添加一個元素厅克,并返回true
   public boolean add(E e) {
        //鏈表尾部添加元素
        linkLast(e);
        return true;
    }

    //向鏈表尾部添加一個元素
    void linkLast(E e) {
        //使用一個局部變量指向鏈表里面的最后一個元素
        final Node<E> l = last;
        //新創(chuàng)建一個node節(jié)點(diǎn),使該新建的節(jié)點(diǎn)的上一個節(jié)點(diǎn)指向之前鏈表中的最后一個節(jié)點(diǎn)
        final Node<E> newNode = new Node<>(l, e, null);
        //因為是在尾部添加節(jié)點(diǎn)橙依,所以新創(chuàng)建的節(jié)點(diǎn)賦予last變量证舟,表示last指向鏈表中的最后一個幾點(diǎn)
        last = newNode;
        //如果l為空,說明之前的鏈表中的節(jié)點(diǎn)元素為空
        if (l == null)
            //所以新創(chuàng)建的節(jié)點(diǎn)賦予first變量窗骑,到這里表示鏈表中僅有一個節(jié)點(diǎn)元素女责,所以該節(jié)點(diǎn)元素即是第一個節(jié)點(diǎn),也是最后一個節(jié)點(diǎn)
            first = newNode;
        else
            //如果l不為空创译,說明之前的鏈表不為空抵知,newNode又是最后一個節(jié)點(diǎn),又因為l為之前舊鏈表的最后一個節(jié)點(diǎn)软族,所以l的下一個節(jié)點(diǎn)指向當(dāng)前新建的節(jié)點(diǎn)
            l.next = newNode;
 
        //size+1刷喜,用來表示當(dāng)前數(shù)組已經(jīng)存儲了多少個節(jié)點(diǎn)元素
        size++;
        modCount++;
    }

具體說明:

  • 當(dāng)往LinkedList添加元素時,實際在鏈表最后添加一個節(jié)點(diǎn)立砸;
  • 當(dāng)往鏈表中添加第一個節(jié)點(diǎn)時掖疮,由于鏈表中僅有一個節(jié)點(diǎn),所以first指向該節(jié)點(diǎn)颗祝,last也指向該節(jié)點(diǎn)浊闪;
  • 當(dāng)往鏈表中添加第二個節(jié)點(diǎn)時,由于鏈表中已經(jīng)有一個節(jié)點(diǎn)螺戳,所以first還是指向鏈表中的第一個節(jié)點(diǎn)搁宾,而新建的第二個節(jié)點(diǎn)的prev指向第一個節(jié)點(diǎn),last指向新建的第二個節(jié)點(diǎn)温峭,并建第一個節(jié)點(diǎn)的next指向第二個節(jié)點(diǎn)猛铅;
如圖所示:
只有一個節(jié)點(diǎn)的時候
當(dāng)有兩個節(jié)點(diǎn)的時候

remove()方法的實現(xiàn)

    public E remove() {
        //刪除鏈表中的第一個節(jié)點(diǎn)
        return removeFirst();
    }

    public E removeFirst() {
        //獲取鏈表的第一個節(jié)點(diǎn)
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        //刪除第一個的節(jié)點(diǎn),并返回刪除的數(shù)據(jù)
        return unlinkFirst(f);
    }

    private E unlinkFirst(Node<E> f) {
        //獲取第一個節(jié)點(diǎn)存儲的數(shù)據(jù)凤藏,用于返回
        final E element = f.item;
        //獲取第一個節(jié)點(diǎn)的之后的下一個節(jié)點(diǎn)奸忽,第一個節(jié)點(diǎn)被刪除后堕伪,下一個節(jié)點(diǎn)就是first節(jié)點(diǎn)
        final Node<E> next = f.next;
        //清空第一個節(jié)點(diǎn)的信息,即清空要刪除節(jié)點(diǎn)的信息
        f.item = null;
        f.next = null; 
        //將原來第一個節(jié)點(diǎn)的下一個節(jié)點(diǎn)賦予first引用栗菜,也就是原來第二個節(jié)點(diǎn)變?yōu)殒湵淼牡谝粋€節(jié)點(diǎn)
        first = next;
        //如果原來第一個節(jié)點(diǎn)的下一個節(jié)點(diǎn)為空欠雌,那么說明鏈表此時已經(jīng)為空了
        if (next == null)
            //此時,將last也置空
            last = null;
        else
            //原來第二個節(jié)點(diǎn)變?yōu)榈谝粋€節(jié)點(diǎn)疙筹,因為第一個節(jié)點(diǎn)前面沒有節(jié)點(diǎn)了富俄,所以將第一個節(jié)點(diǎn)的prev置空
            next.prev = null;
        //size-1,表示鏈表存儲的數(shù)據(jù)-1
        size--;
        modCount++;
        //返回刪除的元素
        return element;
    }

具體說明:

  • 該方法將刪除鏈表中的第一個節(jié)點(diǎn)而咆,并將刪除的數(shù)據(jù)返回霍比。

remove(Object o)方法實現(xiàn)

    //刪除指定元素的節(jié)點(diǎn),刪除成功暴备,返回true悠瞬,否則,返回false
    public boolean remove(Object o) {
        if (o == null) {
            //如果要刪除的元素為null涯捻,那么將刪除鏈表中第一個為null的節(jié)點(diǎn)
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            //刪除鏈表中第一個與傳入值相等的節(jié)點(diǎn)
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

    //刪除指定節(jié)點(diǎn)
    E unlink(Node<E> x) {
        //獲取要刪除節(jié)點(diǎn)的元素的值浅妆,用于刪除后返回
        final E element = x.item;
        //獲取要刪除節(jié)點(diǎn)的下一個節(jié)點(diǎn)
        final Node<E> next = x.next;
         //獲取要刪除節(jié)點(diǎn)的上一個節(jié)點(diǎn)
        final Node<E> prev = x.prev;

        if (prev == null) {
            //如果要刪除的上一個節(jié)點(diǎn)為空,說明當(dāng)前要刪除的節(jié)點(diǎn)為第一個節(jié)點(diǎn)
            //那么需要將first指向要刪除節(jié)點(diǎn)的下一個節(jié)點(diǎn)
            first = next;
        } else {
            //將要刪除的節(jié)點(diǎn)的上一個節(jié)點(diǎn)的引用指向要刪除節(jié)點(diǎn)的下一個節(jié)點(diǎn)
            //比如原來是1,2,3    那么刪除2后障癌,就變成  1,3   也就是將1指向3
            prev.next = next;
            //并將刪除的節(jié)點(diǎn)的上一個指針置null凌外,也就是將2指向1的指針置null
            x.prev = null;
        }

        if (next == null) {
            //如果要刪除的下一個節(jié)點(diǎn)為空,說明當(dāng)前要刪除的節(jié)點(diǎn)為最后節(jié)點(diǎn)
            //那么需要將last指向要刪除節(jié)點(diǎn)的上一個節(jié)點(diǎn)
            last = prev;
        } else {
            //將要刪除的節(jié)點(diǎn)的下一個節(jié)點(diǎn)的引用指向要刪除節(jié)點(diǎn)的上一個節(jié)點(diǎn)
            //比如原來是1,2,3    那么刪除2后涛浙,就變成  1,3   也就是將3指向1
            next.prev = prev;
            //并將刪除的節(jié)點(diǎn)的下一個指針置null康辑,也就是將2指向3的指針置null
            x.next = null;
        }

        //清空刪除節(jié)點(diǎn)的數(shù)據(jù)
        x.item = null;
        //size-1,表示刪除了一個數(shù)據(jù)
        size--;
        modCount++;
        //返回刪除的節(jié)點(diǎn)的元素
        return element;
    }

具體說明:

  • 傳入要刪除的數(shù)據(jù)蝗拿,然后遍歷鏈表晾捏,刪除第一個匹配的數(shù)據(jù)節(jié)點(diǎn);

remove(int index)方法實現(xiàn)

    //刪除指定索引下標(biāo)的元素
    public E remove(int index) {
        //首先哀托,檢查傳入的索引下標(biāo)是否合法惦辛,不合法將拋出IndexOutOfBoundsException
        checkElementIndex(index);

        //node方法根據(jù)index取出要刪除的節(jié)點(diǎn),unlink在上面已經(jīng)分析過仓手,就是刪除指定節(jié)點(diǎn)并返回該節(jié)點(diǎn)的數(shù)據(jù)
        return unlink(node(index));
    }

   //檢查索引是否合法
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
 
    //傳入索引必須大于等于0小于size
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

   //根據(jù)索引下標(biāo)胖齐,變量鏈表,獲取指定下標(biāo)的節(jié)點(diǎn)
    Node<E> node(int index) {
        //判斷如果要刪除的索引小于size/2嗽冒,則從鏈表左邊開始遍歷查找節(jié)點(diǎn)
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            //判斷如果要刪除的索引大于size/2呀伙,則從鏈表右邊開始遍歷查找節(jié)點(diǎn)
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

具體步驟:

  • 傳入要刪除的索引下標(biāo),首先檢查該索引下標(biāo)是否合法添坊;
  • 如果下標(biāo)合法剿另,則調(diào)用node(index)查詢要刪除的節(jié)點(diǎn),如果要刪除的索引下標(biāo)在size的左半部分(index < size/2),那邊將從左邊(first)開始遍歷鏈表查詢雨女,反之從右邊(last)開始查找谚攒;
  • 獲取到要刪除的節(jié)點(diǎn)時,調(diào)用unlink(node)刪除節(jié)點(diǎn)氛堕,并返回節(jié)點(diǎn)數(shù)據(jù)馏臭。

set(int index, E element)方法實現(xiàn)

    //修改指定索引下標(biāo)的節(jié)點(diǎn)的值,并返回修改前的值
    public E set(int index, E element) {
        //檢查index是否合法
        checkElementIndex(index);
        //獲取到index的節(jié)點(diǎn)
        Node<E> x = node(index);
        //獲取到指定下標(biāo)節(jié)點(diǎn)的舊的值
        E oldVal = x.item;
        //替換指定下標(biāo)節(jié)點(diǎn)的值
        x.item = element;
        //返回指定下標(biāo)節(jié)點(diǎn)舊的值
        return oldVal;
    }

具體步驟:

  • 傳入要修改的節(jié)點(diǎn)的索引下標(biāo)與要修改的值讼稚;
  • 檢查下標(biāo)是否合法括儒;
  • 調(diào)用node(index)獲取指定下標(biāo)的節(jié)點(diǎn);
  • 獲取指定下標(biāo)節(jié)點(diǎn)的值锐想,并為該節(jié)點(diǎn)設(shè)置新的值帮寻;
  • 返回指定下標(biāo)節(jié)點(diǎn)替換前的值。

get(int index)方法實現(xiàn)

    //根據(jù)Index獲取數(shù)據(jù)
    public E get(int index) {
        checkElementIndex(index);
        //調(diào)用node獲取數(shù)據(jù)
        return node(index).item;
    }

具體說明:

  • 可以看到痛倚,其實get(index)這種方式獲取數(shù)據(jù)规婆,也是通過調(diào)用node(index)去獲取數(shù)據(jù);

適用場景

通過源碼的分析蝉稳,我們知道,linkedList底層是基于鏈表實現(xiàn)的掘鄙,每個添加一個元素的時候耘戚,都是在鏈表的尾部添加節(jié)點(diǎn),不涉及到鏈表的循環(huán)遍歷操漠;當(dāng)刪除元素時收津,如果調(diào)用無參的remove方法時,只是刪除鏈表的第一個節(jié)點(diǎn)浊伙,也不涉及到鏈表的循環(huán)遍歷撞秋;而當(dāng)需要通過索引下標(biāo)獲取數(shù)據(jù)的時候,涉及到了鏈表的遍歷嚣鄙;所以在這種添加與刪除節(jié)點(diǎn)比較多而根據(jù)索引去訪問元素比較少的場景下吻贿,比較適合使用linkedList。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末哑子,一起剝皮案震驚了整個濱河市舅列,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卧蜓,老刑警劉巖帐要,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異弥奸,居然都是意外死亡榨惠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赠橙,“玉大人伸蚯,你說我怎么就攤上這事〖蚩荆” “怎么了剂邮?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長横侦。 經(jīng)常有香客問我挥萌,道長,這世上最難降的妖魔是什么枉侧? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任引瀑,我火速辦了婚禮衔彻,結(jié)果婚禮上方援,老公的妹妹穿的比我還像新娘。我一直安慰自己床玻,他們只是感情好翼虫,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布屑柔。 她就那樣靜靜地躺著,像睡著了一般珍剑。 火紅的嫁衣襯著肌膚如雪掸宛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天招拙,我揣著相機(jī)與錄音唧瘾,去河邊找鬼。 笑死别凤,一個胖子當(dāng)著我的面吹牛饰序,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播规哪,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼求豫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了由缆?” 一聲冷哼從身側(cè)響起注祖,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎均唉,沒想到半個月后是晨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舔箭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年罩缴,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚊逢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡箫章,死狀恐怖烙荷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情檬寂,我是刑警寧澤终抽,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站桶至,受9級特大地震影響昼伴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜镣屹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一圃郊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧女蜈,春花似錦持舆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惰许,卻和暖如春席覆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背汹买。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留聊倔,地道東北人晦毙。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像耙蔑,于是被迫代替她去往敵國和親见妒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348