Java容器解析——LinkedList

Java容器解析——ArrayList

Java容器解析——LinkedList

Java容器解析——Hashtable

1 LinkedList類定義

LinkedList 是一個繼承于AbstractSequentialList的雙向鏈表啊犬。其定義如下:

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

由定義可以看出:

LinkedList<E>,支持泛型
實現(xiàn)Deque接口召衔,說明可以當(dāng)做隊列
實現(xiàn)Serializable接口, 可序列化
實現(xiàn)Cloneable接口

2 屬性值

//集合元素數(shù)量
transient int size = 0;
//鏈表頭節(jié)點
transient Node<E> first;
//鏈表尾節(jié)點
transient Node<E> last;

節(jié)點結(jié)構(gòu)定義

private static class Node<E> {
        E item;//元素值
        Node<E> next;//后置節(jié)點
        Node<E> prev;//前置節(jié)點

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

由節(jié)點的結(jié)構(gòu)可以看出翔冀,LinkedList是雙向鏈表結(jié)構(gòu)柠并。

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

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

    public LinkedList() {
    
    }

2 使用集合創(chuàng)建

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

addAll()方法

    public boolean addAll(Collection<? extends E> c) {
    
        //以size為插入位置索引贝奇,插入集合c中所有元素
        return addAll(size, c);
    }

    public boolean addAll(int index, Collection<? extends E> c) {
    
        //檢查索引位置是否合法
        checkPositionIndex(index);

        //將目標(biāo)集合c轉(zhuǎn)為數(shù)組
        Object[] a = c.toArray();
        //獲得待添加目標(biāo)數(shù)組的長度
        int numNew = a.length;
        //若長度為0成玫,無需添加绿渣,直接返回
        if (numNew == 0)
            return false;

        //index節(jié)點的前置節(jié)點宴咧,后置節(jié)點
        Node<E> pred, succ;
        
        //在鏈表尾部追加數(shù)據(jù)
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            //取出index節(jié)點根灯,作為后置節(jié)點
            succ = node(index);
            //前置節(jié)點是,index節(jié)點的前一個節(jié)點
            pred = succ.prev;
        }
        
        //變量數(shù)組a掺栅,依次添加節(jié)點
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            //以前置節(jié)點pred 和 元素值e烙肺,構(gòu)建new一個新節(jié)點,
            Node<E> newNode = new Node<>(pred, e, null);
            //如果前置節(jié)點pred是空柿冲,說明是頭結(jié)點
            if (pred == null)
                first = newNode;
            else////前置節(jié)點不為空則將pred的后置節(jié)點設(shè)置為新節(jié)點newNode
                pred.next = newNode;
            pred = newNode;//將當(dāng)前節(jié)點指針向后移動
        }

        //遍歷結(jié)束茬高,到達(dá)鏈表尾,設(shè)置尾節(jié)點
        if (succ == null) {
            last = pred;
        } else {
            //否則是在隊中插入的節(jié)點 假抄,更新前置節(jié)點 后置節(jié)點
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;//更新節(jié)點數(shù)量size
        modCount++;
        return true;
    }

4 核心方法

方法名 含義 時間復(fù)雜度
get(int index) 根據(jù)索引獲取元素 O(n)
add(E e) 添加元素 O(1)
add(int index, E element) 添加元素到指定位置 O(n)
remove(int index) 刪除索引為index的元素 O(n)
set(int index, E element) 設(shè)置索引為index的元素值 O(n)
size() 返回當(dāng)前容器元素大小 O(1)
isEmpty() 判斷容器是否為空 O(1)
contains(Object o) 判斷是否包含某個元素 O(n)
indexOf(Object o) 獲取某元素在列表中索引 O(n)
clear() 清空列表 O(n)

5 獲取元素get()


//根據(jù)索引index獲取元素
public E get(int index) {
    checkElementIndex(index);//判斷是否越界 [0,size)
    return node(index).item; //調(diào)用node()方法 取出 Node節(jié)點怎栽,
}
//根據(jù)index 查詢出Node
Node<E> node(int index) {
        // assert isElementIndex(index);
        // 首先根據(jù)index大小判斷其位置,然后進(jìn)行折半查找宿饱。
        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;
        }
    }

6 添加元素add

(1)直接添加節(jié)點至末尾

    //在尾部插入一個節(jié)點: add
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

    //生成新節(jié)點 并插入到 鏈表尾部熏瞄, 更新 last/first 節(jié)點。
    void linkLast(E e) { 
        final Node<E> l = last; //記錄原尾部節(jié)點
        final Node<E> newNode = new Node<>(l, e, null);//以原尾部節(jié)點為新節(jié)點的前置節(jié)點
        last = newNode;//更新尾部節(jié)點
        if (l == null)//若原鏈表為空鏈表谬以,需要額外更新頭結(jié)點
            first = newNode;
        else//否則更新原尾節(jié)點的后置節(jié)點為現(xiàn)在的尾節(jié)點(新節(jié)點)
            l.next = newNode;
        size++;//修改size
        modCount++;//修改modCount
    }

(2)在指定下標(biāo)位置插入節(jié)點

    //在指定下標(biāo)强饮,index處,插入一個節(jié)點
    public void add(int index, E element) {
        checkPositionIndex(index);//檢查下標(biāo)是否越界[0,size]
        if (index == size)//在尾節(jié)點后插入
            linkLast(element);
        else//在中間插入
            linkBefore(element, node(index));
    }
    //在succ節(jié)點前为黎,插入一個新節(jié)點e
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        //保存后置節(jié)點的前置節(jié)點
        final Node<E> pred = succ.prev;
        //以前置和后置節(jié)點和元素值e 構(gòu)建一個新節(jié)點
        final Node<E> newNode = new Node<>(pred, e, succ);
        //新節(jié)點new是原節(jié)點succ的前置節(jié)點
        succ.prev = newNode;
        if (pred == null)//如果之前的前置節(jié)點是空邮丰,說明succ是原頭結(jié)點。所以新節(jié)點是現(xiàn)在的頭結(jié)點
            first = newNode;
        else//否則構(gòu)建前置節(jié)點的后置節(jié)點為new
            pred.next = newNode;
        size++;//修改數(shù)量
        modCount++;//修改modCount
    }

7 刪除元素remove

(1)刪除索引為index的元素

    //刪:remove目標(biāo)節(jié)點
    public E remove(int index) {
        //檢查是否越界
        checkElementIndex(index);
        //調(diào)用unlink刪除節(jié)點
        return unlink(node(index));
    }
    
    //從鏈表上刪除x節(jié)點
    E unlink(Node<E> x) {
    
        // assert x != null;
        //當(dāng)前節(jié)點的元素值铭乾,設(shè)置為final剪廉,不可更改
        final E element = x.item; 
        //當(dāng)前節(jié)點的后置節(jié)點
        final Node<E> next = x.next; 
        //當(dāng)前節(jié)點的前置節(jié)點
        final Node<E> prev = x.prev;
        
        //如果前置節(jié)點為空(說明當(dāng)前節(jié)點原本是頭結(jié)點)
        if (prev == null) { 
            //則頭結(jié)點為后置節(jié)點 
            first = next;  
        } else { 
            prev.next = next;
            x.prev = null; //將當(dāng)前節(jié)點的 前置節(jié)點置空
        }
        
        //如果后置節(jié)點為空(說明當(dāng)前節(jié)點原本是尾節(jié)點)
        if (next == null) {
            last = prev; //則 尾節(jié)點為前置節(jié)點
        } else {
            next.prev = prev;
            x.next = null;//將當(dāng)前節(jié)點x的后置節(jié)點置空
        }

        x.item = null; //將當(dāng)前元素值置空
        size--; //修改數(shù)量
        modCount++;  //修改modCount
        return element; //返回取出的元素值
    }
        
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

(2)刪除指定的元素

    //因為要考慮 null元素,也是分情況遍歷
    public boolean remove(Object o) {
        if (o == null) {//如果要刪除的是null節(jié)點(從remove和add 里 可以看出炕檩,允許元素為null)
            //遍歷每個節(jié)點 對比
            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;
    }
    //將節(jié)點x斗蒋,從鏈表中刪除
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;//繼續(xù)元素值,供返回
        final Node<E> next = x.next;//保存當(dāng)前節(jié)點的后置節(jié)點
        final Node<E> prev = x.prev;//前置節(jié)點

        if (prev == null) {//前置節(jié)點為null,
            first = next;//則首節(jié)點為next
        } else {//否則 更新前置節(jié)點的后置節(jié)點
            prev.next = next;
            x.prev = null;//記得將要刪除節(jié)點的前置節(jié)點置null
        }
        //如果后置節(jié)點為null泉沾,說明是尾節(jié)點
        if (next == null) {
            last = prev;
        } else {//否則更新 后置節(jié)點的前置節(jié)點
            next.prev = prev;
            x.next = null;//記得刪除節(jié)點的后置節(jié)點為null
        }
        //將刪除節(jié)點的元素值置null捞蚂,以便GC
        x.item = null;
        size--;//修改size
        modCount++;//修改modCount
        return element;//返回刪除的元素值
    }

8 修改元素set

    public E set(int index, E element) {
     //檢查越界[0,size)
        checkElementIndex(index);
        //根據(jù)index取出對應(yīng)的Node
        Node<E> x = node(index);
        //保存舊值 供返回
        E oldVal = x.item;
        //用新值覆蓋舊值
        x.item = element;
        //返回舊值
        return oldVal;
    }

9 小結(jié)

LinkedList 是雙向鏈表,對其大部分操作屬于對雙向鏈表的增刪改查跷究。因此姓迅,對于LinkedList的學(xué)習(xí)主要是掌握數(shù)據(jù)結(jié)構(gòu)鏈表的操作。此外俊马,LinkedList在查找時使用了折半查找的方式队贱,提升了查找效率。

10 對比

(1)ArrayList是實現(xiàn)了基于動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu)潭袱,LinkedList是基于鏈表結(jié)構(gòu)柱嫌。
(2)對于隨機訪問的get和set方法,ArrayList要優(yōu)于LinkedList屯换,因為LinkedList要移動指針编丘。
(3)對于新增和刪除操作add和remove,LinkedList比較占優(yōu)勢彤悔,因為ArrayList要移動數(shù)據(jù)嘉抓。

11 使用場景

(1)對ArrayList和LinkedList而言,在列表末尾增加一個元素所花的開銷都是固定的晕窑。對 ArrayList而言抑片,主要是在內(nèi)部數(shù)組中增加一項,指向所添加的元素杨赤,偶爾可能會導(dǎo)致對數(shù)組重新進(jìn)行分配敞斋;而對LinkedList而言,這個開銷是 統(tǒng)一的疾牲,分配一個內(nèi)部Entry對象植捎。
(2)在ArrayList集合中添加或者刪除一個元素時,當(dāng)前的列表所所有的元素都會被移動阳柔。而LinkedList集合中添加或者刪除一個元素的開銷是固定的焰枢。
(3)LinkedList集合不支持 高效的隨機隨機訪問(RandomAccess),因為可能產(chǎn)生二次項的行為舌剂。
(4)ArrayList的空間浪費主要體現(xiàn)在在list列表的結(jié)尾預(yù)留一定的容量空間济锄,而LinkedList的空間花費則體現(xiàn)在它的每一個元素都需要消耗相當(dāng)?shù)目臻g

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市霍转,隨后出現(xiàn)的幾起案子荐绝,更是在濱河造成了極大的恐慌,老刑警劉巖谴忧,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件很泊,死亡現(xiàn)場離奇詭異,居然都是意外死亡沾谓,警方通過查閱死者的電腦和手機委造,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來均驶,“玉大人昏兆,你說我怎么就攤上這事「狙ǎ” “怎么了爬虱?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長腾它。 經(jīng)常有香客問我跑筝,道長,這世上最難降的妖魔是什么瞒滴? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任曲梗,我火速辦了婚禮,結(jié)果婚禮上妓忍,老公的妹妹穿的比我還像新娘虏两。我一直安慰自己,他們只是感情好世剖,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布定罢。 她就那樣靜靜地躺著,像睡著了一般旁瘫。 火紅的嫁衣襯著肌膚如雪祖凫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天酬凳,我揣著相機與錄音蝙场,去河邊找鬼。 笑死粱年,一個胖子當(dāng)著我的面吹牛售滤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播台诗,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼完箩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拉队?” 一聲冷哼從身側(cè)響起弊知,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎粱快,沒想到半個月后秩彤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叔扼,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年漫雷,在試婚紗的時候發(fā)現(xiàn)自己被綠了瓜富。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡降盹,死狀恐怖与柑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蓄坏,我是刑警寧澤价捧,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站涡戳,受9級特大地震影響结蟋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜渔彰,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一椎眯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胳岂,春花似錦编整、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至产园,卻和暖如春汞斧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背什燕。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工粘勒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屎即。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓庙睡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親技俐。 傳聞我的和親對象是個殘疾皇子乘陪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

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

  • 一.線性表 定義:零個或者多個元素的有限序列。也就是說它得滿足以下幾個條件:??①該序列的數(shù)據(jù)元素是有限的雕擂。??②...
    Geeks_Liu閱讀 2,701評論 1 12
  • 前言 今天來介紹下LinkedList啡邑,在集合框架整體框架一章中,我們介紹了List接口井赌,LinkedList與A...
    嘟爺MD閱讀 3,594評論 11 37
  • 在一個方法內(nèi)部定義的變量都存儲在棧中谤逼,當(dāng)這個函數(shù)運行結(jié)束后贵扰,其對應(yīng)的棧就會被回收,此時流部,在其方法體中定義的變量將不...
    Y了個J閱讀 4,418評論 1 14
  • hashmap實現(xiàn)的數(shù)據(jù)結(jié)構(gòu)戚绕,數(shù)組、桶等贵涵。 如圖所示 JDK 1.7,是以數(shù)組+鏈表組成的恰画,鏈表為相同hash的鍵...
    不需要任何閱讀 829評論 0 1
  • 我是在2月份開始訂閱笑來老師的《通往財富自由之路》宾茂,開始沒有什么明確的目標(biāo),只是單純的想學(xué)點知識∷┗梗現(xiàn)在想來跨晴,雖然遇...
    江上往來ren閱讀 281評論 0 0