吃透Java集合系列四:LinkedList

文章首發(fā)csdn博客地址:https://blog.csdn.net/u013277209?viewmode=contents

前言

本篇作為吃透java集合系列第四篇,主要來(lái)了解一下LinkedList疚鲤,通過(guò)本篇你將了解到如下:
1、LinkedList的List特性
2薄嫡、LinkedList的Queue特性

一:LinkedList

LinkedList類是List接口的實(shí)現(xiàn)類封断,它是一個(gè)集合掀序,可以根據(jù)索引來(lái)隨機(jī)的訪問(wèn)集合中的元素,還實(shí)現(xiàn)了Deque接口烦绳,它還是一個(gè)隊(duì)列卿捎,可以當(dāng)成雙端隊(duì)列來(lái)使用。
雖然LinkedList是一個(gè)List集合径密,但是它的實(shí)現(xiàn)方式和ArrayList是完全不同的午阵,ArrayList的底層是通過(guò)一個(gè)動(dòng)態(tài)的Object[]數(shù)組實(shí)現(xiàn)的,而LinkedList的底層是通過(guò)雙向鏈表來(lái)實(shí)現(xiàn)的享扔,因此它的隨機(jī)訪問(wèn)速度是比較差的底桂,但是它的刪除,插入操作很快惧眠。

  • LinkedList是基于雙向循環(huán)鏈表實(shí)現(xiàn)的籽懦,除了可以當(dāng)作鏈表操作外,它還可以當(dāng)作棧氛魁、隊(duì)列和雙端隊(duì)列來(lái)使用暮顺。
  • LinkedList同樣是非線程安全的,只在單線程下適合使用秀存。
  • LinkedList允許null插入捶码。
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
  • 實(shí)現(xiàn)List接口,具有List集合的特性或链。
  • 實(shí)現(xiàn)Deque接口惫恼,具有隊(duì)列的特性。
  • 實(shí)現(xiàn)Cloneable接口澳盐,可以通過(guò)clone來(lái)實(shí)現(xiàn)淺拷貝
  • 實(shí)現(xiàn)Serializable接口祈纯,可以序列化令宿,通過(guò)writeObject和readObject自定義序列化。

LinkedList的底層結(jié)構(gòu)如下圖所示


在這里插入圖片描述

F表示頭結(jié)點(diǎn)引用腕窥,L表示尾結(jié)點(diǎn)引用粒没,鏈表的每個(gè)結(jié)點(diǎn)都有三個(gè)元素,分別是前繼結(jié)點(diǎn)引用(P)簇爆,結(jié)點(diǎn)元素的值(E)革娄,后繼結(jié)點(diǎn)的引用(N)。結(jié)點(diǎn)由內(nèi)部類Node表示冕碟,我們看看它的內(nèi)部結(jié)構(gòu)。

private static class Node<E> {
        E item;//當(dāng)前元素
        Node<E> next;//下一個(gè)節(jié)點(diǎn)
        Node<E> prev;//上一個(gè)節(jié)點(diǎn)

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

Node這個(gè)內(nèi)部類其實(shí)很簡(jiǎn)單匆浙,只有三個(gè)成員變量和一個(gè)構(gòu)造器安寺,item表示結(jié)點(diǎn)的值,next為下一個(gè)結(jié)點(diǎn)的引用首尼,prev為上一個(gè)結(jié)點(diǎn)的引用挑庶,通過(guò)構(gòu)造器傳入這三個(gè)值。接下來(lái)再看看LinkedList的成員變量和構(gòu)造器软能。

    /**
    * 集合元素個(gè)數(shù)
    */
    transient int size = 0;

    /**
     * 頭結(jié)點(diǎn)
     */
    transient Node<E> first;

    /**
     * 尾節(jié)點(diǎn)
     */
    transient Node<E> last;

    /**
     * 無(wú)參構(gòu)造器
     */
    public LinkedList() {
    }

    /**
     * 傳入外部集合的構(gòu)造器
     */
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

LinkedList持有頭結(jié)點(diǎn)的引用和尾結(jié)點(diǎn)的引用迎捺,它有兩個(gè)構(gòu)造器,一個(gè)是無(wú)參構(gòu)造器查排,一個(gè)是傳入外部集合的構(gòu)造器凳枝。與ArrayList不同的是LinkedList沒(méi)有指定初始大小的構(gòu)造器。

二:LinkedList的List特性

    //增
   /**
    * 將指定的元素追加到此列表的末尾
    */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
   /**
    * 在此列表中的指定位置插入指定的元素跋核。
    */
    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
   /**
    * 將指定集合中的所有元素追加到此列表的末尾岖瑰。
    */
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }
   /**
    * 將指定集合中的所有元素插入到此列表中,從指定的位置開(kāi)始砂代。
    */
    public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0)
            return false;

        Node<E> pred, succ;
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            succ = node(index);
            pred = succ.prev;
        }

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

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

        size += numNew;
        modCount++;
        return true;
    }
   /**
    * 頭插入
    */
    public void addFirst(E e) {
        linkFirst(e);
    }
   /**
    * 尾插入
    */
    public void addLast(E e) {
        linkLast(e);
    }

    //刪
   /**
    * 從列表中刪除指定元素的第一個(gè)出現(xiàn)(如果存在)蹋订。
    */
    public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
   /**
    * 刪除該列表中指定位置的元素。
    */
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
   /**
    * 從此列表中刪除并返回第一個(gè)元素刻伊。
    */
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }
   /**
    * 從此列表中刪除并返回最后一個(gè)元素露戒。
    */
    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }

    //改
   /**
    * 用指定的元素替換此列表中指定位置的元素。
    */
    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

    //查
   /**
    * 返回此列表中指定位置的元素捶箱。
    */
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

LinkedList的添加元素的方法主要是調(diào)用linkLast和linkBefore兩個(gè)方法智什,linkLast方法是在鏈表后面鏈接一個(gè)元素,linkBefore方法是在鏈表中間插入一個(gè)元素讼呢。LinkedList的刪除方法通過(guò)調(diào)用unlink方法將某個(gè)元素從鏈表中移除撩鹿。下面我們看看鏈表的插入和刪除操作的核心代碼。

    /**
     * 返回指定位置的節(jié)點(diǎn)
     */
    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;
        }
    }
    /**
     * 連接到第一個(gè)元素
     */
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

    /**
     * 鏈接到最后一個(gè)元素
     */
    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++;
    }

    /**
     * 在succ前插入元素e
     */
    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++;
    }

    /**
     * 去掉頭結(jié)點(diǎn)
     */
    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;
    }

    /**
     * 去掉尾結(jié)點(diǎn)
     */
    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;
    }

    /**
     * 去掉指定的節(jié)點(diǎn)
     */
    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;
    }

三:LinkedList的Queue特性

通過(guò)對(duì)雙向鏈表的操作還可以實(shí)現(xiàn)單項(xiàng)隊(duì)列悦屏,雙向隊(duì)列和棧的功能节沦。
單向隊(duì)列操作:

//獲取頭結(jié)點(diǎn)
public E peek() {
   final Node<E> f = first;
   return (f == null) ? null : f.item;
}

//獲取頭結(jié)點(diǎn)
public E element() {
   return getFirst();
}

//彈出頭結(jié)點(diǎn)
public E poll() {
   final Node<E> f = first;
   return (f == null) ? null : unlinkFirst(f);
}

//移除頭結(jié)點(diǎn)
public E remove() {
   return removeFirst();
}

//在隊(duì)列尾部添加結(jié)點(diǎn)
public boolean offer(E e) {
   return add(e);
}

雙向隊(duì)列操作:

//在頭部添加
public boolean offerFirst(E e) {
   addFirst(e);
   return true;
}

//在尾部添加
public boolean offerLast(E e) {
   addLast(e);
   return true;
}

//獲取頭結(jié)點(diǎn)
public E peekFirst() {
   final Node<E> f = first;
   return (f == null) ? null : f.item;
}

//獲取尾結(jié)點(diǎn)
public E peekLast() {
   final Node<E> l = last;
   return (l == null) ? null : l.item;
}

棧操作:

//入棧
public void push(E e) {
   addFirst(e);
}

//出棧
public E pop() {
   return removeFirst();
}

不管是單向隊(duì)列還是雙向隊(duì)列還是棧键思,其實(shí)都是對(duì)鏈表的頭結(jié)點(diǎn)和尾結(jié)點(diǎn)進(jìn)行操作,它們的實(shí)現(xiàn)都是基于addFirst()甫贯,addLast()吼鳞,removeFirst(),removeLast()這四個(gè)方法叫搁。

  • LinkedList是基于雙向鏈表實(shí)現(xiàn)的赔桌,不論是增刪改查方法還是隊(duì)列和棧的實(shí)現(xiàn),都可通過(guò)操作結(jié)點(diǎn)實(shí)現(xiàn)
  • LinkedList無(wú)需提前指定容量渴逻,因?yàn)榛阪湵聿僮骷驳常系娜萘侩S著元素的加入自動(dòng)增加
  • LinkedList刪除元素后集合占用的內(nèi)存自動(dòng)縮小,無(wú)需像ArrayList一樣調(diào)用trimToSize()方法
  • LinkedList的所有方法沒(méi)有進(jìn)行同步惨奕,因此它也不是線程安全的雪位,應(yīng)該避免在多線程環(huán)境下使用
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市梨撞,隨后出現(xiàn)的幾起案子雹洗,更是在濱河造成了極大的恐慌,老刑警劉巖卧波,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件时肿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡港粱,警方通過(guò)查閱死者的電腦和手機(jī)螃成,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)查坪,“玉大人锈颗,你說(shuō)我怎么就攤上這事∵浠荩” “怎么了击吱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)遥昧。 經(jīng)常有香客問(wèn)我覆醇,道長(zhǎng),這世上最難降的妖魔是什么炭臭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任永脓,我火速辦了婚禮,結(jié)果婚禮上鞋仍,老公的妹妹穿的比我還像新娘常摧。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布落午。 她就那樣靜靜地躺著谎懦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪溃斋。 梳的紋絲不亂的頭發(fā)上界拦,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音梗劫,去河邊找鬼享甸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛梳侨,可吹牛的內(nèi)容都是我干的蛉威。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼走哺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瓷翻!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起割坠,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妒牙,沒(méi)想到半個(gè)月后彼哼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡湘今,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年敢朱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摩瞎。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拴签,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出旗们,到底是詐尸還是另有隱情蚓哩,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布上渴,位于F島的核電站岸梨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏稠氮。R本人自食惡果不足惜曹阔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隔披。 院中可真熱鬧赃份,春花似錦、人聲如沸奢米。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至园蝠,卻和暖如春渺蒿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背彪薛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工茂装, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人善延。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓少态,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親易遣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子彼妻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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