從源碼分析java容器之LinkedList

引言

上一篇文章咱們一起分析了ArrayList,本篇咱們一起來看看它的姐妹LinkedList,分析的源碼是JDK8版本。

1、LinkedList結構圖

LinkedList繼承了AbstractSequentialList,實現了List、Deque、Cloneable、Serializable接口,如下圖所示:
在這裏插入圖片描述

  • AbstractSequentialList類:繼承AbstractList,從該抽象類的英文註釋中可以知道,該類提供了List接口的骨幹實現,對“順序訪問”數據存儲(如鏈接列表)支持。對於隨機訪問數據(如數組),應該優先使用 AbstractList。
  • Deque 接口:Deque定義了一個線性Collection,支持在兩端插入和刪除元素。
    現在,我們應該知道了LinkedList是一個雙向鏈表結構,支持複製、序列化的。

2、分析源碼

我們分析的順序還是從對象的屬性,構造方法,常用方法進行分析

2.1、屬性

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    transient int size = 0;

    transient Node<E> first;

    transient Node<E> last;
	
	//...省略部分代碼
}

上面源碼中爲LinkedList中的基本屬性,其中size爲LinkedList的長度,first爲指向頭結點,last指向尾結點,Node爲LinkedList的一個私有內部類,其定義如下,即定義了item(元素),next(指向後一個元素的指針),prev(指向前一個元素的指針)。

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

我們假設LinkedList中的元素爲[“A”,”B”,”C”],其內部的結構如下圖所示:
在這裏插入圖片描述
從上圖,我們可以清晰的看出LinkedList底層是雙向鏈表的實現。

2.2、構造方法

public LinkedList() {}

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

源碼中只有兩個構造方法,一個是空構造方法,啥事也沒做,另外一個構造方法可以添加集合元素,轉化爲鏈表,我們可以看到方法是addAll,我們先看添加單個元素的add方法,addAll方法與其大同小異。

2.3、常用方法

2.3.1、新增
public boolean add(E e) {
	linkLast(e);
	return true;
}

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

其實通過源碼可以看出添加的過程如下

1.記錄當前末尾節點,構造另外一個指向末尾節點的指針l
2.產生新的節點:在鏈表的末尾添加,next是爲null的
3.last指向新的節點
4.這裏有個判斷,判斷是否爲第一個元素(當l==null時,表示鏈表中是沒有節點的), 如果是第一節點,則使用first指向這個節點,若不是則當前節點的next指向新增的節點
5.size增加,操作記錄modCount增加

2.3.2、刪除
//方法1.刪除指定索引上的節點
public E remove(int index) {
	//檢查索引是否正確
	checkElementIndex(index);
	//這裏分爲兩步,第一通過索引定位到節點,第二刪除節點
	return unlink(node(index));
}
//方法2.刪除指定值的節點
public boolean remove(Object o) {
	//判斷是否爲null,然後進行遍歷刪除
	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;
}

通過源碼可以看出兩個方法都是通過unlink()刪除,咱們先看方法一中定位到節點的node(index)方法是如何實現的:

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;
        }
    }
  • 先通過二分法,確定index的位置,是靠近first還是靠近last
  • 若靠近first則從頭開始查詢,否則從尾部開始查詢,可以看出這樣避免極端情況的發生,也更好的利用了LinkedList雙向鏈表的特徵

下面再分析刪除節點中最核心的方法unlink()

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;

	/刪除的是第一個節點,first向後移動
	if (prev == null) {
		first = next;
	} else {
		prev.next = next;
		x.prev = null;
	}
	//刪除的是最後一個節點,last向前移
	if (next == null) {
		last = prev;
	} else {
		next.prev = prev;
		x.next = null;
	}

	x.item = null;
	size--;
	modCount++;
	return element;
}
  • 1.獲取到需要刪除元素當前的值,指向它前一個節點的引用,以及指向它後一個節點的引用。
  • 2.判斷刪除的是否爲第一個節點,若是則first向後移動,若不是則將當前節點的前一個節點next指向當前節點的後一個節點
  • 3.判斷刪除的是否爲最後一個節點,若是則last向前移動,若不是則將當前節點的後一個節點的prev指向當前節點的前一個節點
  • 4.將當前節點的值置爲null
  • 5.size減少並返回刪除節點的值

上面就是LinkedList添加、刪除元素的內部實現。

2.4、總結

上一篇我們分析了ArrayList,下面我們來總結對比下ArrayList和LinkedList的區別:

2.4.1、相同點
  • 1.接口實現:都實現了List接口,都是線性列表的實現
  • 2.線程安全:都是線程不安全的
2.4.2、區別
  • 1.底層實現:ArrayList內部是數組實現,而LinkedList內部實現是雙向鏈表結構
  • 2.接口實現:ArrayList實現了RandomAccess可以支持隨機元素訪問,而LinkedList實現了Deque可以當做隊列使用
  • 3.性能:新增、刪除元素時ArrayList需要使用到拷貝原數組,而LinkedList只需移動指針,查找元素 ArrayList支持隨機元素訪問,而LinkedList只能一個節點接一個節點的去遍歷

結束語

寫源碼分析文章是需要耗費巨大的精力的,如果本篇文章對你有幫助,請隨手點個贊,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章