Java實習生面試複習(四):LinkedList

我是一名很普通的大三學生。我將堅持寫博客,輸出知識的同時鞏固自己的基礎,記錄自己的成長和鍛鍊自己,奧利給!!

如果你覺得內容對你有幫助的話,不如給個贊鼓勵一下更新?(σ゚∀゚)σ…:*☆哎喲不錯哦

LinkedList 適用於集合元素先入先出和先入後出的場景,在隊列中被頻繁使用。下面我們就來簡單瞭解一下它,並看看它跟常用的ArrayList的區別。

LinkedList結構分析

LinkedList 底層數據結構是一個雙向鏈表,整體結構如下圖所示:

LinkedList結構
我們看一下Node節點的源碼實現:

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

從上圖和Node節點的源碼中我們可以看出,鏈表中的每個節點都可以向前或者向後遍歷,我們有幾個概念如下:

  • 鏈表每個節點我們叫做 Node,prev 屬性,代表前一個節點的位置,next 屬性,代表後一個節點的位置;
  • first 是雙向鏈表的頭節點,它的前一個節點是 null。
  • last 是雙向鏈表的尾節點,它的後一個節點是 null;
  • 當鏈表中沒有數據時,first 和 last 是同一個節點,前後指向都是 null;
  • 鏈表理論上是沒有大小限制的。

雖然在添加元素的時候沒有“是否超過最大容量”這種判斷,但是 LinkedList 實際大小用的是 int 類型,這也說明了LinkedList 不能超過 Integer 的最大值,不然會溢出。

從底層結構上看,我們可以看出LinkedListArrayList是完全不一樣的,一個是數組實現,一個是鏈表實現。

新增、刪除、查詢操作

追加(新增)

追加節點時,我們可以選擇追加到鏈表頭部,還是追加到鏈表尾部,add 方法默認是從尾部開始追加,addFirst 方法見名知其意,就是從頭部開始追加,我們分別來看下兩種不同的追加方式:

// 從尾部開始追加節點
void linkLast(E e) {
    // 把尾節點數據暫存
    final Node<E> l = last;
    // 新建新的節點,l 是新節點的前一個節點,e 表示當前新增節點,當前新增節點後一個節點是 null
    final Node<E> newNode = new Node<>(l, e, null);
    // 新建節點追加到尾部
    last = newNode;
    //如果鏈表爲空(l 是尾節點,尾節點爲空,鏈表即空),頭部和尾部是同一個節點,都是新建的節點
    if (l == null)
        first = newNode;
    //否則把前尾節點的下一個節點,指向當前尾節點。
    else
        l.next = newNode;
    //大小和版本更改
    size++;
    modCount++;
}

// 從頭部追加
void linkFirst(E e) {
    // 頭節點賦值給臨時變量
    final Node<E> f = first;
    // 新建節點,前一個節點指向null,e 是新建節點,f 是新建節點的下一個節點,目前值是頭節點的值
    final Node<E> newNode = new Node<>(null, e, f);
    // 新建節點成爲頭節點
    first = newNode;
    // 頭節點爲空,就是鏈表爲空,頭尾節點是一個節點
    if (f == null)
        last = newNode;
    //上一個頭節點的前一個節點指向當前節點
    else
        f.prev = newNode;
    size++;
    modCount++;
}

從源碼上來看,頭部追加節點和尾部追加節點非常類似,只是前者是移動頭節點的 prev 指向,後者是移動尾節點的 next 指向。都只需要簡單地把指向位置修改下即可,就偷懶不畫圖了~

刪除

同理,刪除也有從頭部刪除和尾部刪除,且刪除操作會把節點的值,前後指向節點都置爲 null,幫助 GC 進行回收。
這裏就不一一例舉了,就看下從尾部刪除的吧,頭部刪除其實差不多。


    private E unlinkLast(Node<E> l) {
        // l在這裏就是last節點
        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 // 否則把前節點的下一個節點,指向null。
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

到這裏,我們已經可以看出,鏈表結構的節點新增、刪除都非常簡單,僅僅把前後節點的指向修改下,並不像ArrayList可能涉及到數組的複製,這也是爲什麼說LinkedList適合頻繁的增刪。

查詢

我們都知道,其實鏈表查詢某一個節點是比較慢的,因爲它需要挨個循環查找纔行,我們看看 LinkedList是如何尋找節點的

// 根據鏈表索引位置查詢節點
Node<E> node(int index) {
    // 如果 index 處於隊列的前半部分,從頭開始找,size >> 1 是 size 除以 2 的意思。
    if (index < (size >> 1)) {
        Node<E> x = first;
        // 直到 for 循環到 index 的前一個 node 停止
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {// 如果 index 處於隊列的後半部分,從尾開始找
        Node<E> x = last;
        // 直到 for 循環到 index 的後一個 node 停止
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

我們可以看出,LinkedList並沒有死板的採用從頭循環到尾的做法,因爲是雙向鏈表,所以可以從頭或者從尾開始查詢,利用這個特性,可以採取簡單二分法,首先看看index是在鏈表的前半部分,還是後半部分。如果是前半部分,就從頭開始尋找,反之亦然。通過這種方式,使循環的次數至少降低了一半,提高了查找的性能,這種思想值得我們借鑑。

注意:LinkedList 實現了 Queue 接口,在新增、刪除、查詢等方面增加了很多新的方法,這些方法在平時特別容易混淆,在鏈表爲空的情況下,返回值也不太一樣,我們列一個表格,方便大家記錄:

方法含義 返回異常 返回特殊值 底層實現
新增 add(e) offer(e) 底層實現相同
刪除 remove() poll(e) 鏈表爲空時,remove 會拋出異常,poll 返回 null
查找 element() peek() 鏈表爲空時,element 會拋出異常,peek 返回 null

迭代器

LinkedList的迭代器並不是我們常使用的Iterator 接口,因爲要實現雙向的迭代訪問,而Iterator 只支持從頭到尾的訪問。Java 新增了一個迭代接口繼承Iterator,叫做:ListIterator,這個接口提供了向前和向後的迭代方法,如下所示:

迭代順序 方法
從尾到頭迭代方法 hasPrevious、previous、previousIndex
從頭到尾迭代方法 hasNext、next、nextIndex

這裏同樣在循環刪除元素時,也推薦通過迭代器進行刪除

總結

LinkedList適用於要求有順序、並且會按照順序進行迭代的場景,主要是依賴於底層的鏈表結構。

看到這,思考下如下問題:

  • ArrayList 和 LinkedList 有何不同?
  • ArrayList 和 LinkedList 應用場景有何不同?
  • 你能描述下雙向鏈表麼,以及它的新增和刪除?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章