LinkedList原始碼分析(基於JDK8)
LinkedList簡介
LinkedList 是一個繼承於AbstractSequentialList的雙向連結串列。它也可以被當作堆疊、佇列或雙端佇列進行操作。
LinkedList 實現 List 介面,能對它進行佇列操作。
LinkedList 實現 Deque 介面,即能將LinkedList當作雙端佇列使用。
LinkedList 實現了Cloneable介面,即覆蓋了函式clone(),能克隆。
LinkedList 實現java.io.Serializable介面,這意味著LinkedList支援序列化,能通過序列化去傳輸。
LinkedList 是非同步的。
LinkedList相對於ArrayList來說,是可以快速新增,刪除元素,ArrayList新增刪除元素的話需移動陣列元素,可能還需要考慮到擴容陣列長度。
LinkedList屬性
LinkedList本身的 的屬性比較少,主要有三個,一個是size,表名當前有多少個節點;一個是first代表第一個節點;一個是last代表最後一個節點。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為0,first和last的節點都為空:
public LinkedList() {
}
另一個構造方法是帶Collection值得物件作為入參的建構函式的,下面是執行邏輯:
1)使用this()呼叫預設的無參建構函式。
2)呼叫addAll()方法,傳入當前的節點個數size,此時size為0,並將collection物件傳遞進去
3)檢查index有沒有陣列越界的嫌疑
4)將collection轉換成陣列物件a
5)迴圈遍歷a陣列,然後將a數組裡面的元素建立成擁有前後連線的節點,然後一個個按照順序連起來。
6)修改當前的節點個數size的值
7)操作次數modCount自增1.
下面是實現的原始碼:
預設建構函式
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
呼叫帶引數的addAll方法
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
將collection物件轉換成陣列連結串列 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) {
@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;
}
add方法
add(E e)方法
該方法直接將新增的元素放置連結串列的最後面,然後連結串列的長度(size)加1,修改的次數(modCount)加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++;
}
add(int index, E element)方法
指定位置往陣列連結串列中新增元素
1)檢查新增的位置index 有沒有小於等於當前的長度連結串列size,並且要求大於0
2)如果是index是等於size,那麼直接往連結串列的最後面新增元素,相當於呼叫add(E e)方法
3)如果index不等於size,則先是索引到處於index位置的元素,然後在index的位置前面新增新增的元素。
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
把索引到的元素新增到新增的元素之後
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++;
}
get方法
get方法
首先是判斷索引位置有沒有越界,確定完成之後開始遍歷連結串列的元素,那麼從頭開始遍歷還是從結尾開始遍歷呢,這裡其實是要索引的位置與當前連結串列長度的一半去做對比,如果索引位置小於當前連結串列長度的一半,否則從結尾開始遍歷 public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
遍歷連結串列元素
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;
}
}
getfirst方法
直接將第一個元素返回 public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
getlast方法
直接將最後一個元素返回
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
remove方法
remove()方法
remove方法本質呼叫的還是removeFirst方法
public E remove() {
return removeFirst();
}
removeFirst()方法
移除第一個節點,將第一個節點置空,讓下一個節點變成第一個節點,連結串列長度減1,修改次數加1,返回移除的第一個節點。
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
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;
}
removeLast()方法
移除最後一個節點,將最後一個節點置空,最後一個節點的上一個節點變成last節點,,連結串列長度減1,修改次數加1,返回移除的最後一個節點。
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
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;
}
remove(int index)方法
先是檢查移除的位置是否在連結串列長度的範圍內,如果不在則丟擲異常,根據索引index獲取需要移除的節點,將移除的節點置空,讓其上一個節點和下一個節點對接起來。 public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
set方法
檢查設定元素位然後置是否越界,如果沒有,則索引到index位置的節點,將index位置的節點內容替換成新的內容element,同時返回舊值。
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
clear方法
將所有連結串列元素置空,然後將連結串列長度修改成0,修改次數加1 public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}
push和pop方法
push其實就是呼叫addFirst(e)方法,pop呼叫的就是removeFirst()方法。
toArray方法
建立一個Object的陣列物件,然後將所有的節點都新增到Object物件中,返回Object陣列物件。 public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}
listIterator方法
這個方法返回的是一個內部類ListIterator,使用者可以使用這個內部類變數當前的連結串列元素,但是由於LinkedList也是非執行緒安全的類,所以和上一篇文章中的ArrayList原始碼分析(基於JDK8) Iterator一樣,多執行緒下面使用,也可能會產生多執行緒修改的異常。 public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}