成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java LinkedList源碼分析

mylxsw / 1451人閱讀

摘要:經(jīng)常和一起被提及。本文分析的具體實現(xiàn)。其中是雙端隊列接口,所以可以當(dāng)作是棧隊列或者雙端隊隊列。注意最后一個方法,這個方法是在指定的位置插入元素。首先判斷位置是否越界,然后判斷是不是最后一個位置。

簡介

LinkedList是一個常用的集合類,用于順序存儲元素。LinkedList經(jīng)常和ArrayList一起被提及。大部分人應(yīng)該都知道ArrayList內(nèi)部采用數(shù)組保存元素,適合用于隨機訪問比較多的場景,而隨機插入、刪除等操作因為要移動元素而比較慢。LinkedList內(nèi)部采用鏈表的形式存儲元素,隨機訪問比較慢,但是插入、刪除元素比較快,一般認為時間復(fù)雜都是O(1)(需要查找元素時就不是了,下面會說明)。本文分析LinkedList的具體實現(xiàn)。

繼承關(guān)系
public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, java.io.Serializable

LinkedList繼承了一個抽象類AbstractSequentialList,這個類就是用調(diào)用ListIterator實現(xiàn)了元素的增刪查改,比如add方法:

public void add(int index, E element) {
    try {
        listIterator(index).add(element);
    } catch (NoSuchElementException exc) {
        throw new IndexOutOfBoundsException("Index: "+index);
    }
}

不過這些方法在LinkedList中被復(fù)寫了。

LinkedList實現(xiàn)了List、Deque、Cloneable以及Serializable接口。其中Deque是雙端隊列接口,所以LinkedList可以當(dāng)作是棧、隊列或者雙端隊隊列。

內(nèi)部變量
transient int size = 0;
transient Node first;
transient Node last;

總共就三個內(nèi)部變量,size是元素個數(shù),first是指向第一個元素的指針,last則指向最后一個。元素在內(nèi)部被封裝成Node對象,這是一個內(nèi)部類,看一下它的代碼:

private static class Node {
    E item;
    Node next;
    Node prev;
    
    Node(Node prev, E element, Node next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

可以看到這是一個雙向鏈表的結(jié)構(gòu),每個節(jié)點保存它的前驅(qū)節(jié)點和后繼節(jié)點。

私有方法

LinkedList內(nèi)部有幾個關(guān)鍵的私有方法,它們實現(xiàn)了鏈表的插入、刪除等操作。比如在表頭插入:

private void linkFirst(E e) {
    final Node f = first;    //先保存當(dāng)前頭節(jié)點
    //創(chuàng)建一個新節(jié)點,節(jié)點值為e,前驅(qū)節(jié)點為空,后繼節(jié)點為當(dāng)前頭節(jié)點
    final Node newNode = new Node<>(null, e, f);
    first = newNode;    //讓first指向新節(jié)點
    if (f == null)    //如果鏈表原來為空,把last指向這個唯一的節(jié)點
        last = newNode;
    else    ·        //否則原來的頭節(jié)點的前驅(qū)指向新的頭節(jié)點
        f.prev = newNode;
    size++;
    modCount++;
}

其實就是雙向鏈表的插入操作,調(diào)整指針的指向,時間復(fù)雜度為O(1),學(xué)過數(shù)據(jù)結(jié)構(gòu)的應(yīng)該很容易看懂。其它還有幾個類似的方法:

//尾部插入
void linkLast(E e) {
    final Node l = last;
    final Node newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)    //如果鏈表原來為空,讓first指向這個唯一的節(jié)點
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}
//中間插入
void linkBefore(E e, Node succ) {
    // assert succ != null;
    final Node pred = succ.prev;
    final Node newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}
//刪除頭節(jié)點
private E unlinkFirst(Node f) {
    // assert f == first && f != null;
    final E element = f.item;
    final Node next = f.next; //先保存下一個節(jié)點
    f.item = null;    
    f.next = null; // help GC
    first = next;    //讓first指向下一個節(jié)點
    if (next == null)    //如果下一個節(jié)點為空,說明鏈表原來只有一個節(jié)點,現(xiàn)在成空鏈表了,要把last指向null
        last = null;
    else        //否則下一個節(jié)點的前驅(qū)節(jié)點要置為null
        next.prev = null;
    size--;
    modCount++;
    return element;
}
//刪除尾節(jié)點
 private E unlinkLast(Node l) {
    // assert l == last && l != null;
    final E element = l.item;
    final Node prev = l.prev;  //保存前一個節(jié)點
    l.item = null;
    l.prev = null; // help GC
    last = prev;    //last指向前一個節(jié)點
    if (prev == null)    //與頭節(jié)點刪除一樣,判斷是否為空
        first = null;
    else
        prev.next = null;
    size--;
    modCount++;
    return element;
}
//從鏈表中間刪除節(jié)點
 E unlink(Node x) {
    // assert x != null;
    final E element = x.item;
    final Node next = x.next;    //保存前驅(qū)節(jié)點
    final Node prev = x.prev;    //保存后繼節(jié)點

    if (prev == null) {    //前驅(qū)為空,說明刪除的是頭節(jié)點,first要指向下一個節(jié)點
        first = next;
    } else {                //否則前驅(qū)節(jié)點的后繼節(jié)點變?yōu)楫?dāng)前刪除節(jié)點的下一個節(jié)點
        prev.next = next;
        x.prev = null;
    }

    if (next == null) {       //判斷后繼是否為空,與前驅(qū)節(jié)點是否為空的邏輯類似
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }

    x.item = null;
    size--;
    modCount++;
    return element;
}
公開方法

公開的方法幾乎都是調(diào)用上面幾個方法實現(xiàn)的,例如add方法:

public boolean add(E e) {
    linkLast(e);
    return true;
}
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));
}

這些方法的實現(xiàn)都很簡單。注意最后一個方法add(int index, E element),這個方法是在指定的位置插入元素。首先判斷位置是否越界,然后判斷是不是最后一個位置。如果是就直接插入鏈表末尾,否則調(diào)用linkBefore(element, node(index)方法。這里在傳參數(shù)的時候又調(diào)用了node(index),這個方法的目的是找到這個位置的節(jié)點對象,代碼如下:

Node node(int index) {
    // assert isElementIndex(index);
    if (index < (size >> 1)) {
        Node x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

這里有個小技巧是先判斷位置是在鏈表的前半段還是后半段,然后決定從鏈表的頭還是尾去尋找節(jié)點。要注意的是遍歷鏈表尋找節(jié)點的時間復(fù)雜度是O(n),即使做了位置的判斷,最壞情況下也要遍歷鏈表中一半的元素。所以此時插入操作的時間復(fù)雜度就不是O(1),而是O(n/2)+O(1)。用于查找指定位置元素的get(int index)方法便是調(diào)用node實現(xiàn)的:

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

再看一下remove方法:

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

public boolean remove(Object o) {
    if (o == null) {
        for (Node x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}

第一個remove(int index)方法同樣要調(diào)用node(index)尋找節(jié)點。而第二個方法remove(Object o)是刪除指定元素,這個方法要依次遍歷節(jié)點進行元素的比較,最壞情況下要比較到最后一個元素,比調(diào)用node方法更慢,時間復(fù)雜度為O(n)。另外從這個方法可以看出LinkedList的元素可以是null。

總結(jié)

LinkedList基于雙向鏈表實現(xiàn),元素可以為null

LinkedList插入、刪除元素比較快,如果只要調(diào)整指針的指向那么時間復(fù)雜度是O(1),但是如果針對特定位置需要遍歷時,時間復(fù)雜度是O(n)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/65406.html

相關(guān)文章

  • [學(xué)習(xí)筆記-Java集合-2] List - LinkedList源碼分析

    摘要:刪除元素作為雙端隊列,刪除元素也有兩種方式,一種是隊列首刪除元素,一種是隊列尾刪除元素。作為,又要支持中間刪除元素,所以刪除元素一個有三個方法,分別如下。在中間刪除元素比較低效,首先要找到刪除位置的節(jié)點,再修改前后指針,時間復(fù)雜度為。 介紹 LinkedList是一個以雙向鏈表實現(xiàn)的List,它除了作為List使用,還可以作為隊列或者棧來使用,它是怎么實現(xiàn)的呢?讓我們一起來學(xué)習(xí)吧。 繼...

    novo 評論0 收藏0
  • Java集合干貨——LinkedList源碼分析

    摘要:前言在上篇文章中我們對對了詳細的分析,今天我們來說一說。他們之間有什么區(qū)別呢最大的區(qū)別就是底層數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)不一樣,是數(shù)組實現(xiàn)的具體看上一篇文章,是鏈表實現(xiàn)的。至于其他的一些區(qū)別,可以說大部分都是由于本質(zhì)不同衍生出來的不同應(yīng)用。 前言 在上篇文章中我們對ArrayList對了詳細的分析,今天我們來說一說LinkedList。他們之間有什么區(qū)別呢?最大的區(qū)別就是底層數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)不一樣,...

    jsdt 評論0 收藏0
  • LinkedList源碼分析

    摘要:表明該類是可以序列化的。與對比并沒有實現(xiàn),而實現(xiàn)表明其支持快速通常是固定時間隨機訪問。此接口的主要目的是允許一般的算法更改其行為,從而在將其應(yīng)用到隨機或連續(xù)訪問列表時能提供良好的性能。這是隨機訪問效率低的原因之一。指定節(jié)點不能為。 總覽 showImg(https://segmentfault.com/img/bVbsIzr?w=1007&h=600); 定義 public class...

    tommego 評論0 收藏0
  • Java 常用List集合使用場景分析

    摘要:常用集合使用場景分析過年前的最后一篇,本章通過介紹,,,底層實現(xiàn)原理和四個集合的區(qū)別。和都是線程安全的,不同的是前者使用類,后者使用關(guān)鍵字。面試官會認為你是一個基礎(chǔ)扎實,內(nèi)功深厚的人才到這里常用集合使用場景分析就結(jié)束了。 Java 常用List集合使用場景分析 過年前的最后一篇,本章通過介紹ArrayList,LinkedList,Vector,CopyOnWriteArrayList...

    godruoyi 評論0 收藏0
  • LinkedList中查詢(contains)和刪除(remove)源碼分析

    摘要:一源碼分析本文分析雙向鏈表的查詢操作源碼實現(xiàn)。中源程序中,的查詢操作,通過函數(shù)實現(xiàn)。源程序中使用循環(huán)進行遍歷。表示鏈表元素索引,初值為。針對空元素的情況,用循環(huán)遍歷,查找元素為的節(jié)點,并返回索引。 一、contains源碼分析 本文分析雙向鏈表LinkedList的查詢操作源碼實現(xiàn)。jdk中源程序中,LinkedList的查詢操作,通過contains(Object o)函數(shù)實現(xiàn)。具體...

    timger 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<