List
中除了以前講到的ArrayList
外,還有LinkedList
也很是經常使用,以前在學習這兩個類的時候,只是簡單的記錄了它們各自的特性。若是咱們須要屢次添加新元素,不多有從中間get
的操做,那麼就使用LinkedList
,反之則使用ArrayList
。以前咱們在研究ArrayList
源碼時發現,當咱們在添加元素時,若是size = capacity
的時候,那麼就會進行擴容,在擴容的時候就回影響性能,而LinkedList
則不會有這個問題。先來看看定義:java
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
繼承了AbstractSequenttialList
以及實現了List
,Deque
,Clone
,Serializable
接口。實現了List
的基本操做,以及能夠進行clone
和序列化,和ArrayList
最大的區別在於,ArrayList
實現了RandomAccess
接口,而LinkedList
實現的是Deque
雙向隊列(Double-ended queue)接口,雙向隊列即同時具有棧和隊列的性質的數據結構。隊列中的元素能夠從兩端進行操做,也能夠從兩端添加。node
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
的value
Node
的下一個Node
是什麼Node
的上一個Node
是什麼舉個例子來說,有一個LinkedList
,有兩個元素A
和B
,依次添加A
和B
,那麼B
就是A
的next
,而A
就是B
的prev
。dom
LinkedList
有倆構造函數函數
public LinkedList() {}
複製代碼
能夠發現,啥都沒幹,啥都沒幹也就意味着LinkedList
中的兩個屬性Node first
和Node last
都爲null
,即LinkedList
爲空。性能
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
複製代碼
能夠看到,傳入了一個collection
來初始化:學習
this()
來使用空參構造函數來初始化LinkedList
addAll
方法(這個地方的index
就是size
,默認是0)//在 index 所在的node前添加 c 集合節點
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index); // index >=0 && index <=size
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
//保存 index 的節點爲 succ,當前節點的上一個節點爲 pred
Node<E> pred, succ;
//若是 index == size ,說明在尾部進行添加,即當前節點爲null,當前節點的上一個節點爲
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index); // 查找 index 所在的 node
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
//若是 pred 爲null,則說明 LinkedList 爲空,那麼當前節點就爲first
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
//若是succ爲空,則說明是要在尾部進行添加
if (succ == null) {
last = pred;
} else {
//不然pred(即添加的最後一個節點)的下一個節點指向當前節點(index所在的節點)
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
複製代碼
LinkedList
的添加方法包含了add
,addFirst
,addLast
,add(int index,E e)
,add == addLast
,也就是說,咱們能夠在LinkedList
中的任意位置插入一個節點,而全部的add
操做,最終都是調用了下面的兩個方法this
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++;
}
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
複製代碼
實現也就比較簡單了,就不贅述了。spa
LinkedList
的刪除方法包含了remove
,removeFirst
,removeLast
,remove(Object obj)
,其中remove == removeFirst
,默認先會remove
最早進入的節點。和add
方法相似,remove
方法最終也會調用倆方法指針
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
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
prev.next = null;
size--;
modCount++;
return element;
}
複製代碼
值得說明的一點是將待刪除的節點的信息設置爲null
,目的是爲了 釋放頭節點的next/prev
指針和 element
下次 gc 的時候回收這個內部類
LinkedList
的獲取節點的方法也是類似的,分爲了getFirst
,getLast
和get(int index)
,對於getFirst
和getLast
比較簡單,只須要獲取first
或last
的value
就行了。值得一看的是get(int 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;
}
}
複製代碼
看到這個關鍵的if (index < (size >> 1))
,若是想要查找的index
處於size
當前長度的後半段,那麼就從後往前找,不然從前日後着,以此來提升效率。可見,若是咱們須要大量的查找操做,特別是經過index
來查找,那麼推薦使用ArrayList
,而不是LinkedList
。