簡(jiǎn)單聊聊 LinkedList

哈嘍偏序,大家好,今天我們來(lái)簡(jiǎn)單聊聊LinkedList

LinkedList是由雙鏈表組成的集合,它不是線程安全的吩案,如果有在多線程中添加或刪除一個(gè)或多個(gè)元素,需要自己做同步處理帝簇,也可以調(diào)用List list = Collections.synchronizedList(new LinkedList(...));來(lái)獲取一個(gè)線程同步的集合徘郭。


下面我們開(kāi)始簡(jiǎn)單分析一下源碼,首先來(lái)看看LinkedList這個(gè)類(lèi)實(shí)現(xiàn)了哪些關(guān)鍵的接口丧肴。

    public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable

我們可以從源碼中看出LinkedList實(shí)現(xiàn)了List接口來(lái)方便集合操作残揉,同時(shí)也實(shí)現(xiàn)了Deque接口,這樣就有了許多操作鏈表的方法闪湾。

Node

Node類(lèi)是LinkedList的一個(gè)內(nèi)部類(lèi)冲甘,這個(gè)類(lèi)是鏈表中存放元素用的⊥狙看下源碼

    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

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

有三個(gè)參數(shù)江醇,item是當(dāng)前元素的值,next指的是下一個(gè)元素何暇,prev指的是上一個(gè)元素陶夜。

重要參數(shù)

    transient Node<E> first;

    transient Node<E> last;

在LinkedList中有兩個(gè)比較重要的參數(shù),一個(gè)是first裆站,指的是鏈表中第一個(gè)元素条辟。而last指的就是鏈表中最后一個(gè)元素黔夭。

LinkedList()

    public LinkedList() {
    }

這個(gè)LinkedList的一個(gè)空構(gòu)造函數(shù),沒(méi)有做任何其他操作羽嫡。

LinkedList(Collection<? extends E> c)

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

這是一個(gè)帶參數(shù)的構(gòu)造函數(shù)本姥,將傳入進(jìn)來(lái)的集合全部添加到了LinkedList中,addAll這個(gè)方法我們?cè)诤竺孢M(jìn)行講解杭棵。

getFirst()

    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

這個(gè)方法是獲取第一個(gè)元素的值婚惫,這里是直接將first賦值給f,因?yàn)閒irst在LinkedList中就是指的第一個(gè)元素魂爪。如果f為null的話就會(huì)報(bào)錯(cuò)先舷。最后會(huì)返回f的值。

getLast()

    public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }

這個(gè)方法是獲取最后一個(gè)元素的值滓侍,這個(gè)方法和getFirst()方法類(lèi)似蒋川,直接將last賦值給l,最后返回l的值撩笆。

removeFirst()

    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);

    private E unlinkFirst(Node<E> f) {
        //將f的值賦值給element捺球。
        final E element = f.item;
        //將f的下一個(gè)元素賦值給next。
        final Node<E> next = f.next;
        //將f的值和f中的next都設(shè)置為null浇衬,方便GC
        f.item = null;
        f.next = null; // help GC
        //將first設(shè)置為f的next
        first = next;
        //如果next為null懒构,證明鏈表中只有一個(gè)元素,那么將最后一個(gè)元素也設(shè)置為null耘擂。
        if (next == null)
            last = null;
        else
        //如果不為null胆剧,那么將next的prev設(shè)置為null,原本指向的是f醉冤。
            next.prev = null;
        size--;
        modCount++;
        //最后返回f的值秩霍。
        return element;
    }

這個(gè)方法是移除第一個(gè)元素。還是先將first賦值給f蚁阳,然后調(diào)用unlinkFirst方法并將f傳入铃绒。unlinkFirst的相關(guān)解釋寫(xiě)在了代碼中。

removeLast()

    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }

    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        final E element = l.item;
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

這個(gè)方法和removeFirst方法邏輯差不多螺捐,是將最后一個(gè)元素置為null颠悬,然后將倒數(shù)第二個(gè)元素賦值給last。

addFirst(E e)

    public void addFirst(E e) {
        linkFirst(e);
    }

    private void linkFirst(E e) {
        //將first賦值給f定血。
        final Node<E> f = first;
        //根據(jù)傳入的值e赔癌,new一個(gè)新的Node出來(lái),并將f傳入澜沟,設(shè)置為新Node的下一個(gè)元素灾票。
        final Node<E> newNode = new Node<>(null, e, f);
        //將第一個(gè)元素設(shè)置為新的Node。
        first = newNode;
        //如果f為null表示原本鏈表中沒(méi)有元素茫虽,這時(shí)候添加了一個(gè)元
        //素后第一個(gè)刊苍,最后一個(gè)元素都是newNode既们,所以要設(shè)置last為newNode。
        if (f == null)
            last = newNode;
        else
        //否則將原本第一個(gè)元素的prev設(shè)置為newNode正什。
            f.prev = newNode;
        size++;
        modCount++;
    }

在鏈表的頭部添加一個(gè)元素啥纸,關(guān)鍵解釋放在了代碼中。

addLast(E e)

    public void addLast(E e) {
        linkLast(e);
    }

    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++;
    }

addLast和addFirst大致邏輯差不多婴氮。只是將元素加在了鏈表最后脾拆,并重新設(shè)置了last的值。

add(E e)

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

add(E e)方法和addLast(E e)相比只是多了一個(gè)返回值莹妒。

remove(Object o)

    public boolean remove(Object o) {
        //先判斷傳入的o是否為null。
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                //如果為null绰上,則用==來(lái)比較旨怠。
                if (x.item == null) {
                    //調(diào)用unlink方法來(lái)刪除元素。
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                //如果不為null蜈块,則用equals來(lái)比較值鉴腻。
                if (o.equals(x.item)) {
                    //調(diào)用unlink方法來(lái)刪除元素。
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

    E unlink(Node<E> x) {
        // assert x != null;
        //將要?jiǎng)h除元素x的item百揭,next爽哎,prev分別提出來(lái)。
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            //如果x的prev為null器一,證明x為第一個(gè)元素课锌,刪除x后,這里就將next設(shè)置為第一個(gè)元素祈秕。
            first = next;
        } else {
            //如果不為空渺贤,將x元素上一個(gè)元素的next指向x的next。
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            //如果x的next為null请毛,說(shuō)明x為最后一個(gè)元素志鞍,設(shè)置last為x的上一個(gè)元素。
            last = prev;
        } else {
            //如果不為空方仿,將x的下一個(gè)元素的prev指向x的prev固棚。
            next.prev = prev;
            x.next = null;
        }

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

remove(Object o)方法的關(guān)鍵解釋已經(jīng)放在了代碼中。

addAll(Collection<? extends E> c)

    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }

    public boolean addAll(int index, Collection<? extends E> c) {
        //先檢查傳入的index是否越界仙蚜,因?yàn)檫@個(gè)的index默認(rèn)為size此洲,所以會(huì)將c直接添加到鏈表末尾。
        checkPositionIndex(index);

        //將c轉(zhuǎn)為數(shù)組鳍征,并判斷c的長(zhǎng)度黍翎,如果為0,說(shuō)明c為空數(shù)組艳丛,直接返回false匣掸。
        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0)
            return false;

        Node<E> pred, succ;
        if (index == size) {
             //如果index等于size趟紊,說(shuō)明將添加到鏈表末尾,所以pred為last碰酝。
            succ = null;
            pred = last;
        } else {
            //如果index不等于size霎匈,說(shuō)明將添加到鏈表中,將目前index的元素賦值給succ送爸,
            //并將上一個(gè)元素賦值給pred铛嘱。
            succ = node(index);
            pred = succ.prev;
        }

        //通過(guò)for循環(huán)生成新的Node實(shí)例,然后將每一個(gè)新的Node以此添加到pred后面袭厂。
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }

         //添加完成后如果succ為null墨吓,則為在鏈表末尾添加,我們將我們添加的最后一個(gè)元素設(shè)置為last纹磺。
        //如果succ不為null帖烘,則將原本index的下一個(gè)元素添加到prev后面。
        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
    }

addAll(Collection<? extends E> c)方法的關(guān)鍵解釋放在了代碼中橄杨。

clear()

    public void clear() {
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }

clear()方法很簡(jiǎn)單秘症,通過(guò)for循環(huán)依次將元素設(shè)置為null。

get(int index)

    public E get(int index) {
        //檢查index是否越界式矫。
        checkElementIndex(index);
        return node(index).item;
    }

    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    Node<E> node(int 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;
        }
    }

在get(int index)方法中乡摹,會(huì)先判斷index是在鏈表的前一半還是在后一半,然后分別從第一個(gè)元素或者是最后一個(gè)元素來(lái)看是向前或向后查找index對(duì)應(yīng)的元素采转。這樣作會(huì)使查找速度更快聪廉。

set(int index, E element)

    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

這里直接調(diào)用node方法獲取index對(duì)應(yīng)的元素,然后將元素的值替換為element故慈。

add(int index, E element)

    public void add(int index, E element) {
        checkPositionIndex(index);
        
        if (index == size)
            //如果index為size則將element放入新的Node中并添加到鏈表末尾锄列。
            linkLast(element);
        else
            //否則將element放入新的Node中,添加到index對(duì)應(yīng)元素前面惯悠。
            linkBefore(element, node(index));
    }

    void linkBefore(E e, Node<E> succ) {
        //獲取index對(duì)應(yīng)元素的前一個(gè)元素邻邮。
        final Node<E> pred = succ.prev;
        //新的Node位置在index對(duì)應(yīng)元素和前一個(gè)元素之間。
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

add(int index, E element)方法的解釋已經(jīng)放在了代碼中克婶。

remove(int index)

    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

remove(int index)方法先檢查index是否越界筒严,然后調(diào)用node方法獲取index對(duì)應(yīng)的元素,最后調(diào)用unlink方法刪除這個(gè)元素情萤。

indexOf

    public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

先判斷傳入的o是否為null鸭蛙,如果為null則在for循環(huán)中用==來(lái)比較,如果不為null筋岛,則用equals來(lái)比較值娶视,最后返回元素對(duì)應(yīng)的index,如果沒(méi)有對(duì)應(yīng)的元素,則返回-1肪获。

toArray()

    public Object[] toArray() {
        Object[] result = new Object[size];
        int i = 0;
        for (Node<E> x = first; x != null; x = x.next)
            result[i++] = x.item;
        return result;
    }

toArray()方法先生成一個(gè)相同size的數(shù)組寝凌,然后通過(guò)for循環(huán)將鏈表的值全部賦值給數(shù)組,最后返回?cái)?shù)組孝赫。


到這里L(fēng)inkedList的一些基本方法就分析完成了较木,代碼并不是很復(fù)雜,我們通過(guò)分析源碼可以發(fā)現(xiàn)LinkedList在增加青柄,刪除方面代碼很簡(jiǎn)單伐债,相對(duì)應(yīng)的速度也就比較快。在查找致开,修改方面的代碼就比較復(fù)雜峰锁,每次都會(huì)從頭開(kāi)始去找相應(yīng)的元素,速度也就會(huì)比較慢双戳。綜合來(lái)看如果你的需求中有大量的增加祖今,刪除的話可以考慮使用LinkedList。

如果文中有錯(cuò)誤的地方歡迎大家指出來(lái)拣技,也可以留言交流。

3Q

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐ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)容