1. 程式人生 > >Java原始碼--LinkedList原始碼概述

Java原始碼--LinkedList原始碼概述

與ArrayList同為List,LinkedList卻展現出不同的特性。作為java.util下的另一重要容器,我們下面來探究一下LinkedList的原始碼實現及特性分析。

上篇文章講述到,ArrayList用陣列來儲存資料,伴隨資料量的變大,ArrayList動態擴充陣列容量
與之不同,LinkedList使用連結串列來儲存資料,因此它在插入/刪除資料方面有著天然的優勢,而在讀取指定位置的元素時,效能卻不及ArrayList速度快

LinkedList儲存元素的變數如下:

transient Node<E> first;

  儲存資料的節點: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; //後節點
	}
}

 可以看出來,所有元素節點連在一起,構成了雙向連結串列(prev指向前一個元素節點,next指向後一個元素節點)。所有資料,儲存在Node的item中。相比於單向連結串列,雙向連結串列提供了兩種操作的方向,因此可以提供更快的遍歷查詢速度,更快的插入、刪除元素速度

(代價是提升了連結串列維護的難度,以及一點點儲存引用的空間)。

 

下面看一下LinkedList內部對於雙向連結串列的維護:


在此之前,需要知道的一點:LinkedList額外儲存了first和last兩個節點,分別指向首位和末尾,作為雙向連結串列的兩端入口。

內部維護連結串列的一系列函式如下(private、protected):

//用e構造Node,將資料插到首位
private void linkFirst(E e)
{
	final Node<E> f = first; //取得首位節點node
	final Node<E> newNode = new Node<>(null, e, f); //構造節點,item為e,prev為null,next指向當前的首位f
	first = newNode; //將first指向newNode,以它作為新的首位
	if (f == null)
		last = newNode; //若之前的首位f不存在,則新的首位節點newNode在作為first的同時,也是last
	else
		f.prev = newNode; //若之前的首位f存在,則將f的prev指向新的首位節點newNode
	size++;
	modCount++;
}

//用e構造Node,將資料插到末尾
,原理類似 void linkLast(E e); //將節點插入到指定節點前 void linkBefore(E e, Node<E> succ) { final Node<E> pred = succ.prev; //取得原先succ之前的節點pred,現在需要將newNode插在succ之前,pred之後 final Node<E> newNode = new Node<>(pred, e, succ); //構造內容為e的newNode,prev指向pred,next指向succ succ.prev = newNode; //將succ的prev指向newNode if (pred == null) first = newNode; //若pred為空,則將newNode置為first else pred.next = newNode; //將pred的next指向newNode size++; modCount++; } //將首位first節點從連結串列去掉 private E unlinkFirst(Node<E> f); //將末位last節點從連結串列去掉 private E unlinkLast(Node<E> l); //將任意位置的節點x從連結串列去掉 E unlink(Node<E> x) { //分別考慮prev與next為null和非null的情況,修復prev與next的指向 }

  基於上述連結串列操作函式,LinkedList開放了如下介面(public)

public E getFirst()
{
	//取得first內部的item,返回
}

public E getLast()
{
	//取得last內部的item返回
}

public E removeFirst()
{
	//呼叫unlinkFirst(Node<E> f), 將first從連結串列移除
}

public E removeLast()
{
	//呼叫unlinkLast(Node<E> l), 將last從連結串列移除
}

public void addFirst(E e)
{
	//將e插入到first之前
}

public void addLast(E e)
{
	//將e插入到last之後
}

public boolean add(E e)
{
	//呼叫linkLast(e),將e插入到last之後
}

public boolean remove(Object o)
{
	//從first開始遍歷連結串列,找到o,移除節點
}


public int indexOf(Object o)
{
	//從first開始遍歷節點,找到o,返回index
}

public int lastIndexOf(Object o)
{
	//從last開始反向遍歷連結串列,找到o,移除連結串列
}

public boolean contains(Object o)
{
	//從first開始遍歷連結串列,找到o,返回true;或者找不到o,返回false
}

public boolean addAll(Collection<? extends E> c)
{
	//1. 將集合c轉成陣列
	//2. 遍歷陣列,對於每個元素,構造node,鏈在last後面
}

  

總結LinkedList用雙向連結串列維護元素,相比於ArrayList提供了快速的插入、移除資料操作的同時,也比單向連結串列的遍歷查詢速度更快。但是,相比於ArrayList,LinkedList的查詢指定index元素效率低(ArrayList使用陣列儲存資料,可以直接依據索引讀取)。