集合之LinkedList源碼分析

LinkedList是基于雙向鏈表實現(xiàn)的凑耻,相比與內(nèi)部使用數(shù)組的ArrayList而言LinkedList查詢比較慢(因為鏈表不用在連續(xù)的存儲空間)语淘,添加碉输、刪除效率比較高(因為只需要修改前驅(qū)結(jié)點和后繼結(jié)點的指針可以實現(xiàn))。因此诱鞠,對于添加互例,刪除比較多的情況下奢入,推薦使用LinkedList。本文基于android-23源碼分析媳叨。

源碼分析

LinkedList繼承和實現(xiàn)的接口

public class LinkedList<E> extends AbstractSequentialList<E> implements
        List<E>, Deque<E>, Queue<E>, Cloneable, Serializable {}

Cloneable:通過實現(xiàn)clone()方法腥光,能夠?qū)崿F(xiàn)克隆對象关顷;
SerializableLinkedList支持序列化,和反序列化武福,實現(xiàn)Serializble接口之后能夠進行序列化傳輸议双;
LinkedList的構(gòu)造函數(shù)

    public LinkedList() {
        voidLink = new Link<E>(null, null, null);
        voidLink.previous = voidLink;
        voidLink.next = voidLink;
    }

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

LinkedList的構(gòu)造函數(shù)當(dāng)中,會先創(chuàng)建一個Link的對象voidLink結(jié)點捉片,這個LinkLinkedList的一個內(nèi)部類平痰,它維護一個結(jié)點數(shù)據(jù),前驅(qū)結(jié)點以及后繼結(jié)點伍纫,創(chuàng)建出來的voidLinkperviousnext分別指向當(dāng)前創(chuàng)建的Link對象宗雇。
我們先來看看Link類的定義:

private static final class Link<ET> {
        //當(dāng)前節(jié)點數(shù)據(jù)
        ET data;
        //previous前驅(qū)結(jié)點,next后繼結(jié)點
        Link<ET> previous, next;
        Link(ET o, Link<ET> p, Link<ET> n) {
            data = o;
            previous = p;
            next = n;
        }
    }

如果在LinkedList的構(gòu)造函數(shù)中傳遞了collection集合變量莹规,那么最后還會調(diào)用addAll方法赔蒲。

添加元素的方法

@Override
    public boolean addAll(Collection<? extends E> collection) {
        int adding = collection.size();
        if (adding == 0) {
            return false;
        }
        Collection<? extends E> elements = (collection == this) ?
                new ArrayList<E>(collection) : collection;
        Link<E> previous = voidLink.previous;
        for (E e : elements) {
            //創(chuàng)建newLink結(jié)點
            Link<E> newLink = new Link<E>(e, previous, null);
            //previous結(jié)點的后繼結(jié)點指向當(dāng)前創(chuàng)建的結(jié)點
            previous.next = newLink;
            //previous 結(jié)點指向當(dāng)前創(chuàng)建的結(jié)點
            previous = newLink;
        }
        //previous結(jié)點的后繼結(jié)點指向LinkedList的當(dāng)前結(jié)點
        previous.next = voidLink;
        //當(dāng)前結(jié)點的前驅(qū)結(jié)點指向previous結(jié)點
        voidLink.previous = previous;
        //當(dāng)前的數(shù)量增加
        size += adding;
        //當(dāng)前修改的標記增加
        modCount++;
        return true;
    }

addAll的方法中,先拿到需要添加的元素的集合访惜,然后循環(huán)遍歷嘹履,在遍歷的過程中不斷創(chuàng)建新的結(jié)點,然后將前一個結(jié)點的后繼結(jié)點指向當(dāng)前創(chuàng)建的結(jié)點债热,然后將前一個結(jié)點指向當(dāng)前創(chuàng)建的結(jié)點,循環(huán)完了之后幼苛,將前一個結(jié)點的后繼結(jié)點指向LinkedList中的當(dāng)前結(jié)點窒篱,然后將當(dāng)前結(jié)點的前驅(qū)結(jié)點指向前一個結(jié)點,修改sizemodCount的值舶沿,完成添加工作墙杯。
現(xiàn)在我們看看其他的add方法:

  @Override
    public boolean add(E object) {
        return addLastImpl(object);
    }

   public void addLast(E object) {
        addLastImpl(object);
   }
   
    private boolean addLastImpl(E object) {
        //LinkedList的voidLink的前驅(qū)結(jié)點
        Link<E> oldLast = voidLink.previous;
        //創(chuàng)建一個newLink結(jié)點,newLink的前驅(qū)結(jié)點指向oldLast括荡,后繼結(jié)點指向voidLink
        Link<E> newLink = new Link<E>(object, oldLast, voidLink);
        //voidLink的前驅(qū)結(jié)點指向newLink
        voidLink.previous = newLink;
        //oldLast的后繼結(jié)點指向newLink
        oldLast.next = newLink;
        size++;
        modCount++;
        return true;
    }

oldLast結(jié)點指向voidLink結(jié)點的前驅(qū)結(jié)點高镐,創(chuàng)建一個newLink結(jié)點,newLink的前驅(qū)結(jié)點指向oldLast畸冲,后繼結(jié)點指向voidLink嫉髓,然后將voidLink的前驅(qū)結(jié)點指向newLinkoldLast的后繼結(jié)點指向newLink邑闲,這樣操作過后算行,就將新創(chuàng)建的newLink結(jié)點插入到oldLastvoidLink中間。

    public void addFirst(E object) {
        addFirstImpl(object);
    }

    private boolean addFirstImpl(E object) {
        //oldFirst指向voidLink的后繼結(jié)點
        Link<E> oldFirst = voidLink.next;
        //創(chuàng)建newLink結(jié)點
        Link<E> newLink = new Link<E>(object, voidLink, oldFirst);
        //voidLink的后繼結(jié)點指向newLink結(jié)點
        voidLink.next = newLink;
        //oldFirst的前驅(qū)結(jié)點指向newLink結(jié)點
        oldFirst.previous = newLink;
        size++;
        modCount++;
        return true;
    }

首先oldFirst指向voidLink的后繼結(jié)點苫耸,然后創(chuàng)建newLink結(jié)點州邢,voidLink的后繼結(jié)點指向newLink結(jié)點,最后oldFirst的前驅(qū)結(jié)點指向newLink結(jié)點褪子,并且修改size量淌,modCount的值骗村。

 public void addLast(E object) {
        addLastImpl(object);
 }

 private boolean addLastImpl(E object) {
        //oldLast結(jié)點指向voidLink的前驅(qū)結(jié)點
        Link<E> oldLast = voidLink.previous;
        //創(chuàng)建newLink結(jié)點,前驅(qū)結(jié)點為oldLast呀枢,后繼結(jié)點為voidLink
        Link<E> newLink = new Link<E>(object, oldLast, voidLink);
        //
        voidLink.previous = newLink;
        oldLast.next = newLink;
        size++;
        modCount++;
        return true;
 }
 @Override
    public boolean addAll(int location, Collection<? extends E> collection) {
        if (location < 0 || location > size) {
            throw new IndexOutOfBoundsException();
        }
        int adding = collection.size();
        if (adding == 0) {
            return false;
        }
        Collection<? extends E> elements = (collection == this) ?
                new ArrayList<E>(collection) : collection;
        //previous 指向voidLink
        Link<E> previous = voidLink;
        if (location < (size / 2)) {
            for (int i = 0; i < location; i++) {
                //將previous結(jié)點指向previous的后繼結(jié)點
                previous = previous.next;
            }
        } else {
            for (int i = size; i >= location; i--) {
                //將previous結(jié)點指向previous的前驅(qū)結(jié)點
                previous = previous.previous;
            }
        }
        //next結(jié)點指向previous的next
        Link<E> next = previous.next;
        //通過foreach語法糖循環(huán)遍歷元素的集合
        for (E e : elements) {
            //創(chuàng)建newLink結(jié)點叙身,前驅(qū)結(jié)點為previous,后繼結(jié)點為null
            Link<E> newLink = new Link<E>(e, previous, null);
            //previous結(jié)點的后繼結(jié)點指向newLink
            previous.next = newLink;
            //previous指向newLink
            previous = newLink;
        }
        //previous結(jié)點的后繼結(jié)點指向next
        previous.next = next;
        //next結(jié)點的前驅(qū)結(jié)點指向previous
        next.previous = previous;
        size += adding;
        modCount++;
        return true;
    }

添加的方法硫狞,我們主要就看這些信轿,當(dāng)然還有另外的一些添加元素的方法,

    public void push(E e) {
        addFirstImpl(e);
    }

另外的残吩,我們就不多介紹了财忽,下面我們來看看移除元素的方法

刪除方法

移除第一個元素

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

    private E removeFirstImpl() {
        //first結(jié)點指向voidLink的后繼結(jié)點
        Link<E> first = voidLink.next;
        //判斷是否有可移除的結(jié)點
        if (first != voidLink) {
            //next結(jié)點指向first的后繼結(jié)點
            Link<E> next = first.next;
            //voidLink的后繼結(jié)點指向next
            voidLink.next = next;
            //next結(jié)點的前驅(qū)結(jié)點指向voidLink
            next.previous = voidLink;
            size--;
            modCount++;
            return first.data;
        }
        throw new NoSuchElementException();
    }

通過location移除元素

  @Override
    public E remove(int location) {
        if (location >= 0 && location < size) {
            Link<E> link = voidLink;
            //通過location找到link結(jié)點
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            Link<E> previous = link.previous;
            Link<E> next = link.next;
            //修改引用
            previous.next = next;
            next.previous = previous;
            size--;
            modCount++;
            return link.data;
        }
        throw new IndexOutOfBoundsException();
    }

remove(int location)的時候,先判斷location是否越界泣侮,如果沒有越界則繼續(xù)往下執(zhí)行即彪,通過循環(huán)找到link結(jié)點,然后分別拿到link結(jié)點的前驅(qū)結(jié)點和后繼結(jié)點活尊,讓他們分別修改應(yīng)用的指向隶校,從而達到從鏈表中刪除結(jié)點的目的。

@Override
    public void clear() {
        if (size > 0) {
            size = 0;
            //重置voidLink結(jié)點的前驅(qū)結(jié)點蛹锰,后繼結(jié)點指向
            voidLink.next = voidLink;
            voidLink.previous = voidLink;
            //修改modCount變量
            modCount++;
        }
    }

clear方法會重置voidLink結(jié)點的前驅(qū)結(jié)點深胳,后繼結(jié)點指向。

獲取元素

@Override
    public E get(int location) {
        if (location >= 0 && location < size) {
            Link<E> link = voidLink;
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            return link.data;
        }
        throw new IndexOutOfBoundsException();
    }

相比較ArrayList這類可以使用index下標獲取元素的數(shù)組來說铜犬,LinkedList通過location來獲取元素舞终,它需要循環(huán)遍歷,這種隨機的獲取元素的方法效率比較低癣猾。

LinkedList的Iterator

先來看看ReverseLinkIterator:

 private class ReverseLinkIterator<ET> implements Iterator<ET> {
        private int expectedModCount;
        private final LinkedList<ET> list;
        private Link<ET> link;
        private boolean canRemove;
        ReverseLinkIterator(LinkedList<ET> linkedList) {
            list = linkedList;
            expectedModCount = list.modCount;
            link = list.voidLink;
            canRemove = false;
        }
        public boolean hasNext() {
            return link.previous != list.voidLink;
        }
        public ET next() {
            if (expectedModCount == list.modCount) {
                if (hasNext()) {
                    link = link.previous;
                    canRemove = true;
                    return link.data;
                }
                throw new NoSuchElementException();
            }
            throw new ConcurrentModificationException();
        }
        public void remove() {
            if (expectedModCount == list.modCount) {
                if (canRemove) {
                    Link<ET> next = link.previous;
                    Link<ET> previous = link.next;
                    next.next = previous;
                    previous.previous = next;
                    link = previous;
                    list.size--;
                    list.modCount++;
                    expectedModCount++;
                    canRemove = false;
                    return;
                }
                throw new IllegalStateException();
            }
            throw new ConcurrentModificationException();
        }
    }
private static final class LinkIterator<ET> implements ListIterator<ET> {
        int pos, expectedModCount;
        final LinkedList<ET> list;
        Link<ET> link, lastLink;
        LinkIterator(LinkedList<ET> object, int location) {
            list = object;
            expectedModCount = list.modCount;
            if (location >= 0 && location <= list.size) {
                // pos ends up as -1 if list is empty, it ranges from -1 to
                // list.size - 1
                // if link == voidLink then pos must == -1
                link = list.voidLink;
                if (location < list.size / 2) {
                    for (pos = -1; pos + 1 < location; pos++) {
                        link = link.next;
                    }
                } else {
                    for (pos = list.size; pos >= location; pos--) {
                        link = link.previous;
                    }
                }
            } else {
                throw new IndexOutOfBoundsException();
            }
        }
        public void add(ET object) {
            if (expectedModCount == list.modCount) {
                Link<ET> next = link.next;
                Link<ET> newLink = new Link<ET>(object, link, next);
                link.next = newLink;
                next.previous = newLink;
                link = newLink;
                lastLink = null;
                pos++;
                expectedModCount++;
                list.size++;
                list.modCount++;
            } else {
                throw new ConcurrentModificationException();
            }
        }
        public boolean hasNext() {
            return link.next != list.voidLink;
        }
        public boolean hasPrevious() {
            return link != list.voidLink;
        }
        public ET next() {
            if (expectedModCount == list.modCount) {
                LinkedList.Link<ET> next = link.next;
                if (next != list.voidLink) {
                    lastLink = link = next;
                    pos++;
                    return link.data;
                }
                throw new NoSuchElementException();
            }
            throw new ConcurrentModificationException();
        }
        public int nextIndex() {
            return pos + 1;
        }
        public ET previous() {
            if (expectedModCount == list.modCount) {
                if (link != list.voidLink) {
                    lastLink = link;
                    link = link.previous;
                    pos--;
                    return lastLink.data;
                }
                throw new NoSuchElementException();
            }
            throw new ConcurrentModificationException();
        }
        public int previousIndex() {
            return pos;
        }
        public void remove() {
            if (expectedModCount == list.modCount) {
                if (lastLink != null) {
                    Link<ET> next = lastLink.next;
                    Link<ET> previous = lastLink.previous;
                    next.previous = previous;
                    previous.next = next;
                    if (lastLink == link) {
                        pos--;
                    }
                    link = previous;
                    lastLink = null;
                    expectedModCount++;
                    list.size--;
                    list.modCount++;
                } else {
                    throw new IllegalStateException();
                }
            } else {
                throw new ConcurrentModificationException();
            }
        }
        public void set(ET object) {
            if (expectedModCount == list.modCount) {
                if (lastLink != null) {
                    lastLink.data = object;
                } else {
                    throw new IllegalStateException();
                }
            } else {
                throw new ConcurrentModificationException();
            }
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敛劝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子纷宇,更是在濱河造成了極大的恐慌夸盟,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件像捶,死亡現(xiàn)場離奇詭異上陕,居然都是意外死亡,警方通過查閱死者的電腦和手機作岖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門唆垃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人痘儡,你說我怎么就攤上這事辕万。” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵渐尿,是天一觀的道長醉途。 經(jīng)常有香客問我,道長砖茸,這世上最難降的妖魔是什么隘擎? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮凉夯,結(jié)果婚禮上货葬,老公的妹妹穿的比我還像新娘。我一直安慰自己劲够,他們只是感情好震桶,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著征绎,像睡著了一般蹲姐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上人柿,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天柴墩,我揣著相機與錄音,去河邊找鬼凫岖。 笑死江咳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的隘截。 我是一名探鬼主播扎阶,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼婶芭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起着饥,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤犀农,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后宰掉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呵哨,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年轨奄,在試婚紗的時候發(fā)現(xiàn)自己被綠了孟害。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡挪拟,死狀恐怖挨务,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤谎柄,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布丁侄,位于F島的核電站,受9級特大地震影響朝巫,放射性物質(zhì)發(fā)生泄漏鸿摇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一劈猿、第九天 我趴在偏房一處隱蔽的房頂上張望拙吉。 院中可真熱鬧,春花似錦揪荣、人聲如沸筷黔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽必逆。三九已至,卻和暖如春揽乱,著一層夾襖步出監(jiān)牢的瞬間名眉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工凰棉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留损拢,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓撒犀,卻偏偏與公主長得像福压,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子或舞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

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

  • 一.線性表 定義:零個或者多個元素的有限序列荆姆。也就是說它得滿足以下幾個條件:??①該序列的數(shù)據(jù)元素是有限的。??②...
    Geeks_Liu閱讀 2,693評論 1 12
  • 一、基本數(shù)據(jù)類型 注釋 單行注釋:// 區(qū)域注釋:/* */ 文檔注釋:/** */ 數(shù)值 對于byte類型而言...
    龍貓小爺閱讀 4,257評論 0 16
  • 源碼版本JDK1.8 今天帶來的是List的另一種實現(xiàn)——LinkedList诈豌,這是一種基于雙向鏈表實現(xiàn)的列表仆救。接...
    Wangheguan閱讀 244評論 0 0
  • 黃山松 你生長在深澗峻嶺上 與峭壁泉石山花結(jié)鄰 婆娑不改你偉岸的身影 孤貞恥屈腰愈見你蒼勁 凌云,是你的姿態(tài) 傲雪...
    郭大牛閱讀 449評論 6 4
  • 認識一個在七年之癢里的老男人矫渔,有個六歲的女兒彤蔽,剛經(jīng)歷出軌,回頭庙洼,放棄情人所在的但是自己有事業(yè)城市顿痪,回到老家不遠的小...
    尋找遙遠的相似性閱讀 182評論 0 2