概述
LinkedList實現了List接口,在之前使用ArrayList的地方可以替換成LinkedList直接使用。與ArrayList相比,LinkedList(沒有實現RandomAccess接口)底層使用鏈表實現數據的存儲,鏈表相對於數組,在查找獲取方面不能直接通過索引獲取,只能遍歷獲取,速度較慢。但相對於數組實現的ArrayList在插入和刪除數據的時候,不存在容量擴容,也不需要數據的copy平移,速度上ArrayList快。
LinkedList 實現了Deque接口,Deque爲一個雙端隊列接口,定義了隊列,雙端隊列,棧操作的相關方法。也就是LinkedList可以用作隊列,和棧容器。
類繼承關係圖:
LinkedList 實現了Iterable接口,說明該類可以通過迭代器遍歷他。
LinkedList 實現了Collection接口,說明該類具有集合常規方法操作。
LinkedList 實現了List接口,說明該類具有線性表操作方法。
LinkedList實現了Queue/Deque接口,說明該類可以用作棧,隊列,和雙端隊列。
LinkedList實現了Cloneable接口,說明該類具有clone方法。
LinkedList實現了Serializable接口,說明該類可以進行序列化。
成員變量:
//容器中元素個數
transient int size = 0;
//鏈表頭部節點(指針)
transient Node<E> first;
//鏈表尾部節點(指針)
transient Node<E> last;
節點內部類:
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
public LinkedList() {
}
//構造一個包含指定集合元素的LinkedList
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
List接口相關方法:
新增元素
//在鏈表尾部加入一個元素
public boolean add(E e) {
linkLast(e);
return true;
}
//在鏈表尾部加入一個node元素
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; //鏈表容量爲空的時候加入一個元素 //first==last==newNode
else
l.next = newNode;//鏈表不爲空,則直接鏈接到last元素後邊
//容量加一
size++;
//修改modCount 用於迭代器遍歷
modCount++;
}
//在鏈表指定接點加入一個node元素
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;//succ前節點爲空,則將first節點賦值newNode
else
pred.next = newNode;//succ前節點不爲空則直接將新加節點鏈接到前節點的後繼節點
size++;//容量加一
modCount++;//修改modCount 用於迭代器遍歷
}
//指定添加一個元素
public void add(int index, E element) {
//校驗index是否越界
checkPositionIndex(index);
if (index == size)
linkLast(element);//index爲最後一個元素,鏈接到鏈表尾部
else
linkBefore(element, node(index));//index不爲最後一個元素,則將新元素鏈接到該指定位置
}
//校驗索引是否越界,越界拋出異常
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//判斷索引位置是否合法
boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
//添加指定集合中的元素到LinkedList
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) {
@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;//pred指向當前最新節點元素
}
if (succ == null) {//index插入位置爲鏈表尾部則last指向最後一個新增元素節點
last = pred;
} else {//index插入位置爲鏈表其他位置,則pred後繼節點指向斷開節點
pred.next = succ;
succ.prev = pred; 斷開節點前繼節點指向添加元素位置
}
size += numNew;//LinkedList數量增加numNew
modCount++;//修改modCount 用於迭代器遍歷
return true;
}
刪除元素
//刪除指定元素
public boolean remove(Object o) {
//LinkedList允許元素爲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;
}
//從鏈表中移除某個節點
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;//移除節點爲鏈表頭部,則頭節點直接指向next節點
} else {
prev.next = next;//移除節點不爲鏈表頭部,則移除節點的前繼節點的後繼節點指向next節點
x.prev = null;//將移除節點的前繼節點置NULL
}
//處理移除節點的後繼節點
if (next == null) {//移除節點爲鏈表尾部,則將尾部節點直接指向移除節點的前繼節點
last = prev;
} else {//移除節點不爲鏈表尾部,則移除節點的後繼節點的前繼節點指向移除節點的後繼節點
next.prev = prev;
x.next = null;//將移除節點的後繼節點置NULL
}
//移除節點數據端置NULL便於垃圾回收
x.item = null;
size--;//數量減一
modCount++;//修改modCount值
return element;
}
//清空LinkedList
public void clear() {
//從頭節點遍歷置空節點
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;//LinkedList大小置空
modCount++;//修改modCount值
}
查找元素
//獲取指定位置元素
public E get(int index) {
//校驗索引是否合法
checkElementIndex(index);
//調用node方法獲取元素數據
return node(index).item;
}
//檢驗索引是否在合理範圍,不合理拋出IndexOutOfBoundsException異常
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//真正校驗索引範圍
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
//獲取指定索引位置節點方法
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;
}
}
修改元素
//修改指定位置元素
public E set(int index, E element) {
//校驗索引位置是否合法
checkElementIndex(index);
//獲取指定索引節點
Node<E> x = node(index);
//節點數據賦值
E oldVal = x.item;
x.item = element;
return oldVal;
}
迭代器
//獲取指定範圍的迭代器,且可以正向和反向迭代遍歷
public ListIterator<E> listIterator(int index) {
//校驗索引是否在合法範圍
checkPositionIndex(index);
return new ListItr(index);
}
//新版本迭代器內部類
private class ListItr implements ListIterator<E> {
//迭代遍歷返回的當前臨時節點(next方法中使用,用於緩存當前節點),初始值爲null,迭代器迭代遍歷開始後指向容器當前節點值。
private Node<E> lastReturned;
//當前節點 next方法調用後指向下一個節點
private Node<E> next;
//當前節點的索引值,next方法調用後指向下一個節點索引值,用於判斷是否還有數據
private int nextIndex;
//迭代器遍歷時候判斷容器是否發生過變化
private int expectedModCount = modCount;
//指定索引位置的構造方法
ListItr(int index) {
// assert isPositionIndex(index);
//index==size 則next爲空,表示迭代器處於遍歷結束狀態,否則等於node方法返回值,node方法內部有index越界判斷,越界則拋出異常
next = (index == size) ? null : node(index);
//當前節點索引值,next方法調用後指向下一個節點索引值
nextIndex = index;
}
//判斷正向遍歷是否還有元素
public boolean hasNext() {
return nextIndex < size;
}
//正向迭代遍歷取值
public E next() {
//校驗LinkedList是否修改過 若修改過拋出異常
checkForComodification();
//若正向遍歷沒有值則拋異常
if (!hasNext())
throw new NoSuchElementException();
//緩存當前節點引用,用於節點元素值返回
lastReturned = next;
//next節點指向下個節點
next = next.next;
//nextIndex索引指向下個節點索引
nextIndex++;
//返回當前節點數據元素
return lastReturned.item;
}
//是否可以進行前向迭代
public boolean hasPrevious() {
return nextIndex > 0;
}
//前向遍歷取值
public E previous() {
//校驗LinkedList是否修改過,若修改過拋出異常
checkForComodification();
//若前向遍歷無值,拋出異常
if (!hasPrevious())
throw new NoSuchElementException();
//緩存當前節點引用,用於節點元素值返回。此外對next賦值next的前驅節點,若next==null,
//則賦值當前容器的尾部節點(本人分析代碼邏輯不會賦值尾部節點)
lastReturned = next = (next == null) ? last : next.prev;
//nextIndex索引指向前向節點索引
nextIndex--;
//返回當前值
return lastReturned.item;
}
//獲取當前節點索引值
public int nextIndex() {
return nextIndex;
}
//獲取前向節點索引值
public int previousIndex() {
return nextIndex - 1;
}
//通過迭代器移除索引指向元素
public void remove() {
//校驗容器狀態,是否修改過
checkForComodification();
//校驗當前迭代器節點值,沒有開始迭代,移除元素拋出異常
if (lastReturned == null)
throw new IllegalStateException();
//臨時緩存迭代器當前節點的下一個節點
Node<E> lastNext = lastReturned.next;
//移除當前節點,調用容器的刪除方法()
unlink(lastReturned);
//處理迭代器狀態(next==lastReturned邏輯,比較繞,發現調用next方法後,再調用previous方法,會出現此種邏輯,兩者反向調用也會出現此種情況),
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;//移除當前節點後,lastRetunrned置空,垃圾回收
expectedModCount++;//修改容器狀態變化標誌,標識容器發生了變化
}
//通過迭代器更新元素
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
//直接賦值
lastReturned.item = e;
}
//通過迭代器新增元素
public void add(E e) {
//校驗容器狀態發生變化
checkForComodification();
//當前返回節點值置空
lastReturned = null;
//此處邏輯和LinkedList容器新增邏輯相同
if (next == null)
linkLast(e);//當前遍歷器處於鏈表尾部,直接向鏈表尾部插入數據
else
linkBefore(e, next);//迭代器處於鏈表其他位置,則向指定節點前插入新節點
nextIndex++;//迭代器索引指向next所指向數據
expectedModCount++;//修改容器變化狀態標誌,標識容器發生了變化
}
//jdk1.8新加 jdk lambda表達式
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (modCount == expectedModCount && nextIndex < size) {
action.accept(next.item);
lastReturned = next;
next = next.next;
nextIndex++;
}
checkForComodification();
}
//校驗LinkedList是否發生變化(新增元素,刪除元素)
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
//獲取老版本迭代器
public Iterator<E> descendingIterator() {
return new DescendingIterator();
}
//兼容1.6之前版本的老版本迭代器 該迭代器,就是對新迭代器的包裝,只能進行正向遍歷
private class DescendingIterator implements Iterator<E> {
private final ListItr itr = new ListItr(size());
public boolean hasNext() {
return itr.hasPrevious();
}
public E next() {
return itr.previous();
}
public void remove() {
itr.remove();
}
}
其它方法
//鏈表轉化爲數組
public Object[] toArray() {
//new 一個新數組
Object[] result = new Object[size];
int i = 0;
//遍歷將數據copy到數組
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}
//序列化標誌Id
private static final long serialVersionUID = 876323262645176354L;
//自定義序列化方法
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject();
// Write out size
s.writeInt(size);
// Write out all elements in the proper order.
for (Node<E> x = first; x != null; x = x.next)
s.writeObject(x.item);
}
//自定義反序列化方法
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
// Read in size
int size = s.readInt();
// Read in all elements in the proper order.
for (int i = 0; i < size; i++)
linkLast((E)s.readObject());
}
棧操作接口方法
//元素進棧方法
public void push(E e) {
addFirst(e);
}
//在鏈表頭部加入新節點
public void addFirst(E e) {
linkFirst(e);
}
//真正鏈表頭部加入節點方法
private void linkFirst(E e) {
final Node<E> f = first;
//構造新加入元素節點,新節點後繼節點指向f節點,前繼節點爲null
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
//當容器爲空的時候,last尾部節點==first==newNode
if (f == null)
last = newNode;
else
f.prev = newNode;//之前的頭結點的前繼節點指向新加入節點
size++;//修改容器當前數量
modCount++;//累加容器修改狀態值
}
//元素出棧方法
public E pop() {
return removeFirst();
}
//從鏈表頭部取頭節點
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
//頭節點指向next節點
first = next;
if (next == null)
last = null;//最後一個元素出棧,會走此邏輯
else
next.prev = null;//前繼節點置空
//(first節點的前繼節點都爲空)
size--;//容量減一
modCount++;//累加,容器修改狀態
return element;//返回出棧元素
}
隊列操作接口方法
//向隊列中加入元素
public boolean offer(E e) {
//內部調用add方法
return add(e);
}
//向隊列加入元素此爲list接口定義方法
public boolean add(E e) {
//在鏈表尾部加入一個元素
linkLast(e);
return true;
}
//在鏈表尾部加入一個元素
public boolean add(E e) {
linkLast(e);
return true;
}
//在鏈表尾部加入一個node元素
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; //鏈表容量爲空的時候加入一個元素 //first==last==newNode
else
l.next = newNode;//鏈表不爲空,則直接鏈接到last元素後邊
//容量加一
size++;
//修改modCount 用於迭代器遍歷
modCount++;
}
//從隊列頭部獲取一個元素,並移除,當隊列爲空會拋出異常
public E remove() {
//內部調用removeFirst
return removeFirst();
}
//移除鏈表第一個節點
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;
}
//該函數和remove函數功能基本一致,只是當隊列爲空,返回null值
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//從隊列中獲取第一個元素,但不移除該元素,當隊列爲空返回null值
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//從隊列中獲取第一個元素,但不移除該元素,當隊列爲空拋出異常
public E element() {
return getFirst();
}
//真正的獲取第一個元素
public E getFirst() {
final Node<E> f = first;
//隊列會空拋出異常
if (f == null)
throw new NoSuchElementException();
return f.item;
}
雙端隊列接口方法
//向雙端隊列中頭部加入一個元素
public void addFirst(E e) {
linkFirst(e);//鏈表頭部加入節點
}
//向雙端隊列尾部加入一個元素
public void addLast(E e) {
linkLast(e);//鏈表尾部加入節點
}
//向雙端隊列中頭部加入一個元素,和Addfirst方法一樣只是加入了返回值
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
//向雙端隊列中頭部加入一個元素,和addLast方法一樣只是加入了返回值
public boolean offerLast(E e) {
addLast(e);
return true;
}
//從雙端隊列頭部獲取一個元素並移除該元素,當隊列爲空拋出異常
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
//從雙端隊列尾部獲取一個元素,並移除該元素,當隊列爲空拋出異常
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
//從雙端隊列頭部獲取一個元素,並移除該元素,當隊列爲空返回null
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//從雙端隊列尾部獲取一個元素,並移除該元素,當隊列爲空返回null
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
//從隊列頭部獲取元素,不移除該元素,隊列爲空拋出異常
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
//從隊列尾部獲取元素,不移除該元素,隊列爲空拋出異常
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
//從隊列頭部獲取元素,不移除該元素,隊列爲空返回NULL
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//從隊列尾部部獲取元素,不移除該元素,隊列爲空返回NULL
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
從雙端隊列中移除某個元素,正向向遍歷鏈表刪除
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
//從雙端隊列中移除某個元素,反向遍歷鏈表刪除
public boolean removeLastOccurrence(Object o) {
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
總結:
LinkedList底層由雙向鏈表實現,可以輕鬆實現數據插入,和刪除(比數組實現的ArrayList 效率高),不需要擴容,但查找訪問效率比數組慢。
LinkedList實現了List接口,在使用ArrayList的地方可以替換使用。
LinkedList實現了Deque接口,可以用作隊列,棧,雙端隊列。
LinkedList 不支持多線程,通過迭代器遍歷的時候,如果有其他線程刪除或新增元素,會拋出 ConcurrentModificationException()異常。
LinkedList 遍歷刪除元素,最好通過迭代器實現。LinkedList有兩種迭代器,一種只能單向迭代,最新的一種可以雙向,並且指定迭代範圍。
LinkedList實現了自己的序列化方法。