1.回顧
在上一篇介紹了Java 集合之ArrayList主要講解了ArrayList的一些方法和具體實現,ArrayList是基於陣列來實現,當插入新元素時,其後面的元素的位置都需要移動,這顯而易見是個影響效能的操作,資料量一大,再頻繁的執行插入操作那…照例我們先看集合的結構圖
2.結構
LinkedList和ArrayList 同屬於List介面的,看下詳細的結構圖實現類,那麼兩者的方法應該大致相同
AbstractSequentialList類是 AbstractList的一個子類,提供了一個基本的list介面實現,為了順序訪問的資料儲存結構(連結串列)提供了最小化的實現。AbstractSequentiaList是在迭代器基礎上實現的get、set、add等方法。
Deque介面繼承Queue介面,兩端都允許插入和刪除元素,即雙向佇列。
實現了Cloneable,能被克隆,實現了Serializable,支援序列化
我們檢視下LinkedList類中的方法
順便附上AbstractSequentiaList抽象類方法
通過檢視原始碼發現AbstractSequentiaList是在迭代器基礎上實現的get、set、add等方法,而這個迭代是在子類去實現
public abstract ListIterator<E> listIterator(int index);
複製程式碼
3.原始碼分析
一.成員變數
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
複製程式碼
- size 元素個數
- first 指向頭節點
- last 指向尾節點
Node是個內部類
private static class Node<E> {
E item; //結點的值
Node<E> next; //結點的後向指標
Node<E> prev; //結點的前向指標
//構造方法中已完成Node成員的賦值
Node(Node<E> prev, E element, Node<E> next) {
this.item = element; //結點的值賦值為element
this.next = next; //後向指標賦值
this.prev = prev; //前向指標賦值
}
}
複製程式碼
二.構造方法
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
複製程式碼
空構造方法構造了一個空的List 其中size為0 first和last都為null ,沒有任何元素
LinkedList(Collection<? extends E> c)構造一個包含指定Collection中所有元素的列表 該方法先呼叫空構造器 然後addAll()把Collection中所有元素新增進去
三.常用方法
addAll
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//檢查是否越界
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {////for迴圈結束後,a裡面的元素都新增到當前連結串列裡面,後向新增
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
複製程式碼
此方法較長 主要操作就是檢查index是否越界 將collection轉化成陣列 迴圈陣列將陣列裡面的元素建立為節點 並按照順序連起來
修改當前節點個數size
add
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++;
}
複製程式碼
add 方法直接呼叫了 linkLast 方法,而 linkLast 方法是不對外開放的。該方法做了三件事情,新增一個節點,改變其前後引用,將 size 和 modCount 自增 1。其中 modCount 是記錄對集合操作的次數。
remove
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
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;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
複製程式碼
檢查下標是否越界,然後呼叫 unlink 釋放節點。
還有其他方法這裡就不一一列舉了
四 總結
結合原始碼我們大致可以知道LinkedList裡維持了一個連結串列 每個連結串列單元是一個Node Node的prev指向前一個節點 next指向後一個節點 這樣所有節點都串了起來 如圖
有幾點需要注意的是
- LinkedList的實現是基於雙向連結串列的,且頭結點中不存放資料
- LinkedList是基於連結串列實現的,因此不存在容量不足的問題,所以這裡沒有擴容的方法 不同與陣列實現的ArrayList
- LinkedList是基於連結串列實現的,插入刪除效率高,查詢效率低
若經常查詢又很少插入和刪除 則推薦使用ArrayList
相反經常插入與刪除 很少查詢 則推薦使用LinkedList
日常開發中,當然是ArrayList的使用頻率更高些。下一篇準備講解下Set系列…