LinkedList類中的方法與實現原理
目錄
- public boolean add(Object o)
- public boolean addAll(Collection c)
- public boolean contains(Object elem)
- public boolean remove(Object o)
- public void add(int index,Object element)
- public boolean addAll(int index,Collection c)
- public void clear()
- public boolean containsAll(Collection c)
- public boolean equals(Object o)
- public Object get(int index)
- public int hashCode()
- public int indexOf(Object elem)
- public boolean isEmpty()
- public int lastIndexOf(Object elem)
- public ListIterator listIterator(final int index)
- public ListIterator listIterator()
- public Iterator descendingIterator()
- public Object remove(int index)
- public boolean removeAll(Collection c)
- public boolean retainAll(Collection c)
- public Object set(int index,Object element)
- public List subList(int fromIndex,int toIndex)
- public Object[] toArray()
- public Object[] toArray(Object []a)
- public void addFirst(Object element)
- public void addLast(Object element)
- public Object getFirst()
- public Object getLast()
- public Object removeFirst()
- public Object removeLast()
一.資料結構
LinkedList與ArrayList一樣實現List介面,只是ArrayList是List介面的大小可變陣列的實現,LinkedList是List介面連結串列的實現[2],LinkedList在記憶體中開闢的記憶體不連續。基於連結串列實現的方式使得LinkedList在插入和刪除時更優於ArrayList,而隨機訪問則比ArrayList遜色些.【注1】JDK1.7之前版本LinkedList採用迴圈雙向連結串列實現,1.7及之後版本採用雙向連結串列實現
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節點的結構圖如下:
LinkedList是由很多這樣的節點構成,其中:
☞ prev儲存的是上一個節點的引用
☞ item儲存的是具體內容
☞ next儲存的是下一個節點的引用
LinkedList底層資料結構圖如下圖所示:
二.類標題
LinkedList類的標題如下:public class LinkedList extends AbstractSequentialList implements List,Deque,Cloneable.java.io.Serializable
這個標題說明LinkedList類是AbstractSequentialList類的子類,並且實現了四個介面:List、Deque、Cloneable和Serializable。如下圖所示:
1.LinkedList是一個繼承於AbstractSequentialList的雙向連結串列。它也可以被當做堆疊、佇列或者雙端佇列進行操作
2.LinkedList實現List介面,能對它進行佇列操作。
3.LinkedList實現了Deque介面,即能將LinkedList當做雙端佇列使用。
4.LinkedList實現了Cloneable介面,即覆蓋了函式clone(),能克隆。
5.LinkedList實現java.io.Serializable介面,LinkedList支援序列化,能通過序列化去傳輸。
6.LinkedList是非同步的[2]。
【注2】在這裡的非同步指的是,當使用執行緒的時候,對於這個集合物件進行操作,那麼不同的執行緒所獲取的這個集合物件是不同的.所以是說不同步,在多執行緒的形式是不安全的.
三.欄位
transient Node
first
指向第一個節點的指標
transient Node
last
指向最後一個節點的指標
private transient int size
記錄節點的個數
protected transient int modCount = 0;
繼承自AbstractList,用於記錄集合修改次數
四.建構函式
4.1 無參的構造方法,原始碼如下:
public LinkedList() {
}
4.2 將現有集合元素C加入連結串列進行構造,原始碼如下:
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
傳入一個集合Collection作為引數初始化LinkedList.addAll方法原始碼分析詳見下述public boolean addAll(Collection c)
五.方法分析
共有方法
public boolean add(Object o)
作用:在LinkedList的末尾插入元素
原始碼如下:
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++;
}
實現步驟如下:
① 因為我們需要把該元素設定為為節點,所以需要新建一個變數把為節點儲存起來。② 然後新建一個節點,把last指向l,然後設定為尾結點。
③ 在判斷一下l是否為空,如果為空的話,說明原來的LinkedList為空,所以同時也需要把新節點設定為頭結點,否則就把l的next設定為newNode。
④ size和modCount自增。
原始碼分析:
一.當LinkedList為空時,即當沒有頭結點時:
二.當LinkedList不為空,即有頭結點時:
【注】區域性變數需要顯式設定初始化值。成員變數不需要
public boolean addAll(Collection c)
作用:將作為引數傳遞給次函式的集合中的所有元素追加到列表的末尾
原始碼如下:
//通過呼叫addAll(int index,Collection <? extends E> c)完成集合的新增
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
//當索引值不合法時,丟擲越界異常
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//判斷索引值是否合法,在0~size之間,合法返回1,否則返回-1
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
//計算指定索引上的節點(返回Node)
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {//比較index更靠近連結串列(LinkedList)的頭結點還是為節點
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 boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//幾乎所有的涉及在指定位置新增或者刪除修改操作都需要判斷傳進來的引數是否合法。checkPositionIndex(index)就是這個作用。
Object[] a = c.toArray();//將集合轉化為陣列,然後為該陣列新增一個新的引用(Object []a)
int numNew = a.length;//新建變數儲存陣列長度
if (numNew == 0)//如果待新增的集合為空,直接返回,無須進行後面的步驟,後面都是用來把集合中的元素新增到LinkedList中
return false;
Node<E> pred, succ;//pred:指代待新增節點的前一個節點。succ:指代待新增節點的位置
if (index == size) {//待新增的元素位置位於LinkedList最後一個元素後面
succ = null;
pred = last;
} else {//待新增的元素位置位於LinkedList中
succ = node(index);
pred = succ.prev;
}
//遍歷陣列中的每個元素,在每次遍歷的時候,都會新建一個節點,該節點的值儲存陣列a中遍歷的值,該節點的prev用來儲存pred節點,next設定為空,接著判斷一下該節點的前一個節點是否為空,如果為空的話,則把當前節點設定為頭結點,否則的話就把當前節點的前一個節點的next值設定為當前節點,最後那pred指向當前節點,以便後續新節點的新增
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;
}
//當新新增的節點位於LinkedList集合的最後一個元素後面,通過遍歷上面的a的所有元素,此時pred指向的是LinkedList中的最後一個元素,所以把last指向pred指向的節點。
//當不為空的時候,表明在LinkedList集合中新增的元素,需要把pred的next指向succ上,succ的prev指向pred
//最後把集合的大小設定為新的大小
//modCount(修改的次數)自增
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
原始碼分析:
(1) Node node(int index)原始碼分析:
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)//進行遍歷,獲取相應的節點
x = x.next;
return x;
}
作用:當索引值更靠近頭結點使,從頭結點開始遍歷
原理:size >> 1 等價於size/2,即當索引值小於size/2時(意味著距離頭結點更近),因此從頭結點開始遍歷
(2) addAll(int index,Collection c)原始碼分析:
addAll新增集合方法分為兩種情況,一種是在LinkedList列表的末尾新增,一種是在列表中新增。所分情況具體如下圖所示:一.在LinkedList列表尾部新增集合
1.1 待新增節點位置前一個節點為空,此場景處理方式如下圖所示:
1.2 待新增節點位置前一個節點非空,此場景處理方式如下圖所示:
二. 在LinkedList列表中新增集合
2.1 待新增節點位置前一個節點為空,(同上1.1)
2.2 待新增節點位置前一個節點非空,此場景處理方式如下圖所示:
public boolean contains(Object elem)
作用:判斷LinkedList是否包含某一個元素。
原始碼如下:
public boolean contains(Object o) {
return indexOf(o) != -1;
}
原始碼分析:
底層通過呼叫indexOf().(原始碼分析詳見下述public int indexOf(Object elem))。該方法主要用於計算元素在LinkedList中的位置。indexOf()方法非常簡單:首先根據object是否為空,分為兩種情況;然後通過在每種情況下,從頭結點開始遍歷LinkedList.判斷是否有於object相等的元素,如果有,則返回對應的位置index,如果找不到,則返回index,則contains()返回true,否則返回false.public boolean remove(Object o)
作用:移除第一次出現的元素。(從前往後遍歷集合)
原始碼如下:
public boolean remove(Object o) {
//如果物件為null值
if (o == null) {
//從第一個節點開始,逐一往後遍歷,直到x節點為null
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {//如果節點item值為null值,呼叫unlink(x)方法
unlink(x);
return true;
}
}
} else {
//遍歷這個連結串列,找出連結串列中的元素值item與o相同的,移除掉
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;//若集合中沒有此元素,返回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;
} 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(Node x)原始碼分析:
當要unlink()的是頭結點(prev == null),將first指向頭結點的後一個節點。若unlink()是尾結點,將last指向尾結點的前一個節點,否則,將當前節點x的前一個節點的next直接指向X的下一個節點,後一個節點的prev直接指向X的前一個節點。
二.remove(Objec o)原始碼分析:
分待移除元素為空與非空為兩種情況進行處理,通過遍歷找出目標節點,然後底層呼叫unlink(Node x)進行移除。實現List介面方法
public void add(int index,Object element)
作用:在LinkedList連結串列指定位置新增元素
原始碼如下:
public void add(int index, E element) {
checkPositionIndex(index);//檢查索引是否越界,原始碼詳見上述:public boolean addAll(Collection c)
if (index == size)//若新新增的元素位於LinkedList的最後,呼叫linkLast()新增
linkLast(element);//linkLast()原始碼詳見上述:public boolean add(Object o)
else
linkBefore(element, node(index));//在LinkedList連結串列的index位置插入包含元素
}
//在非空節點succ之前插入元素e
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++;
}
一.linkBefore(E e,Node succ)原始碼分析:
☞ 首先需要新建一個變數指向succ節點的前一個節點,因為我們要在succ前面插入一個節點。
☞ 接著新建一個節點,它的prev設定為我們剛才新建的變數,後置節點設定為succ。
☞ 然後修改succ的prev為新節點。
☞ 接著判斷一個succ的前一個節點是否為空,如果為空的話,需要把新節點設定為為頭結點。
☞ 如果不為空,則把succ的前一個節點的next設定為新節點。
實現原理如下圖所示:
二.add(int index,E element)原始碼分析:
當在LinkedList連結串列表尾新增元素,底層呼叫linkLast()方法 在LinkedList連結串列其他索引處新增元素,底層呼叫linkBefore()方法public boolean addAll(int index,Collection c)
作用:在指定位置新增集合元素。
原始碼及原始碼分析詳見上述public boolean addAll(Collection c)
public void clear()
作用:清空LinkedList中的所有元素
原始碼如下:
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++;
}
原始碼分析:
直接遍歷整個LinkedList,然後把每個節點都置空。最後要把頭結點和尾結點設定為空,size也設定為空,但是modCount仍然自增。public boolean containsAll(Collection c)
作用:檢測LinkedList是否包含指定集合中的所有元素
原始碼如下(同ArrayList):
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))//contains(Object)原始碼詳見上述:public boolean contains(Object elem)
return false;
return true;
}
原始碼分析:
LinkedList類的containsAll(Collection c)繼承自AbstractCollection類。 該方法用for-each遍歷c集合,同時檢視c集合中是否包含e元素。 若包含,則返回ture.否則返回false.public boolean equals(Object o)
作用:這裡呼叫的equals方法並不是Object類中的。LinkedList繼承了AbstractList< E>,它的equals方法應該從這個抽象類中來的。發現該類確實重寫了equals方法,就是使用迭代器遍歷兩個List每個元素是否相等。 那麼LinkedList呼叫equals方法就是元素進行比較了。
原始碼如下(同ArrayList):
//inheried from java.util.AbstractList
public boolean equals(Object o) {
if (o == this)//如果o為呼叫此方法的物件,則返回true
return true;
if (!(o instanceof List))//若物件o不是List物件,則返回false
return false;
//定義兩個List迭代器用來遍歷元素
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {//當兩個迭代器均有下一個元素
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
原始碼分析:
if (!(o1 == null ? o2==null : o1.equals(o2)))
return false
若o1為空,返回o2 == null.則變為if(!(o2null));此時,若o2不為null,則直接返回false.
若o1非空,返回o1.equals(o2).則變為if(!(o1.equals(o2)))
【注】此處的equals()方法應為Object類中的equals()方法.若o1與o2不等,則返回false.
return !(e1.hasNext() || e2.hasNext());
若e1和e2都遍歷完了,未返回false,則認定equals()返回true.
public Object get(int index)
作用:返回連結串列中指定位置的元素
原始碼如下:
public E get(int index) {
checkElementIndex(index);
return node(index).item;//呼叫node()返回目標節點的值。node(int index)原始碼詳見上述:public boolean addAll(Collection c)
}
//判斷引數index是否是元素的索引(如果不是則丟擲異常)
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//判斷index索引值是否合法(0 <= index < size)
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
原始碼分析:
首先通過checkElementIndex()檢查索引值是否合法。然後通過node()獲取目標節點的元素值。【注】在LinkedList集合中checkElementIndex()與checkPositionIndex()均為判斷索引值知否合法,但是判斷的索引範圍有細微區別:checkPositionIndex()包含index<=size,而checkElementIndex()不包含
public int hashCode()
作用:求LinkedList的雜湊值,這個方法是LinkedList的抽象父類AbstractList中的方法
原始碼如下(同ArrayList):
//inherited from java.util.AbstractList
public int hashCode() {
int hashCode = 1;
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}
原始碼分析:
其實這段程式碼就是如下數學表示式的實現:s[0]31^(n-1) + s[1]31^(n-2) + … + s[n-1]
在JAVA語言中,判斷兩個物件是否相等,一般有兩種方法,一種是hashcode(),另一種是equals(),這兩個方法在判斷準確性和效率上有很大的區別。hashCode()方法和equal()方法的作用其實一樣,在Java裡都是用來對比兩個物件是否相等一致,那麼equal()既然已經能實現對比的功能了,為什麼還要hashCode()呢?
因為重寫的equal()裡一般比較全面比較複雜,這樣效率就比較低,而利用hashCode()進行對比,則只要生成一個hash值進行比較就可以了,效率很高,那麼hashCode()既然效率這麼高為什麼還要equal()呢?
因為hashCode()並不是完全可靠,有時候不同的物件他們生成的hashcode也會一樣(生成hash值得公式可能存在的問題),所以hashCode()只能說是大部分時候可靠,並不是絕對可靠。
public int indexOf(Object elem)
作用:返回連結串列中元素第一次出現的位置下標,如果沒有此元素則返回-1.
原始碼如下:
public int indexOf(Object o) {
int index = 0;
//根據Object是否為空,分為兩種情況
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {//遍歷,查詢連結串列中是否包含null元素
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
原始碼分析:
首先依據obejct是否為空,分為兩種情況: 然後通過在每種情況下,從頭節點開始遍歷LinkedList,判斷是否有與object相等的元素,如果有,則返回對應的位置index,如果找不到,則返回-1。public boolean isEmpty()
作用:判斷此列表中是否為空,空則返回true,否則返回false.
原始碼如下:
public boolean isEmpty() {
return size == 0;
}
原始碼分析:
繼承AbstractCollection中的isEmpty()方法,如果記錄陣列元素個數的size==0,表示陣列中沒有元素,所以返回true.否則,返回false.public int lastIndexOf(Object elem)
作用:返回此連結串列中元素最後一次出現的位置下標,如果沒有則返回-1.
原始碼如下:
public int lastIndexOf(Object o) {
int index = size;//從後往前遍歷
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
}
原始碼分析:
分查詢元素為空與非空兩種情況進行討論。從後往前遍歷,只返回第一次出現的元素索引,如果沒有找到,則返回-1。public ListIterator listIterator(final int index)
作用:返回從指定索引開始進行操作的迭代器。
原始碼如下:
public ListIterator<E> listIterator(final int index) {
rangeCheckForAdd(index);//判斷索引是否越界,越界丟擲異常
return new ListItr(index);
}
//連結串列迭代器
private class ListItr implements ListIterator<E> {
//迭代器最近一次返回的節點
private Node<E> lastReturned;
//迭代器即將返回的節點
private Node<E> next;
//迭代器即將要返回的節點的索引
private int nextIndex;
//通過內部類變數expectedModCount儲存外部類變數modCount的值,以此保證外部類與內部類的快速失敗機制的同步,在內部類方法執行的時候,ModCount如果改變,那麼必然與expectedNodCount的值不等,直接觸發快速失敗機制
private int expectedModCount = modCount;
//構造方法,建立一個從指定索引index開始的迭代器
ListItr(int index) {
//如果index == size,則next為null,因為索引size處沒有節點。否則,next是索引值為index的節點
next = (index == size) ? null : node(index);
//初始化變數nextIndex,索引為index的節點就是即將要返回的節點
nextIndex = index;
}
public boolean hasNext() {//檢查迭代器中當前節點是否還有下個一節點
return nextIndex < size;
}
//返回節點的元素,迭代的方向是從左至右
public E next() {
checkForComodification();//同ArrayList;檢查是否有其他執行緒修改了LinkedList連結串列
if (!hasNext())
throw new NoSuchElementException();
//next已經在構造方法中進行了初始化,如果next()方法順利執行完畢,next引用的節點元素就會被返回,同時斷開next與節點的引用關係,此節點稱為迭代器最近一次返回的節點,所以在斷開前需要將節點的引用傳遞給lastReturned,否則節點會被垃圾回收期回收
lastReturned = next;
//斷開next引用,指向下一個節點,此時lastReturned引用迭代器最後一次返回的節點
next = next.next;
nextIndex++;
return lastReturned.item;
}
public boolean hasPrevious() {//檢查當前節點是否有前一個節點
return nextIndex > 0;
}
//返回節點的元素,迭代的方向是從右往左
public E previous() {
checkForComodification();//檢查連結串列是否發生結構性修改
if (!hasPrevious())
throw new NoSuchElementException();
//從右往左迭代,找出最近一次返回的節點
lastReturned = next = (next == null) ? last : next.prev;
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);
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
//用包含元素e的節點代替節點
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
}
//新增節點
public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null)
linkLast(e);
else
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
//根據條件過濾連結串列
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();
}
final void checkForComodification() {//快速失敗機制
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
原始碼分析:
迭代器中定義了一些列操作,擁有迭代器的集合,遍歷時不需要知道集合內部實現,迭代器統一了遍歷方法,不同集合各自遍歷方法可能不同,而迭代器則在上層提供了一個統一的抽象,只需要掌握一種迭代器使用就能操作多種不同型別的集合了。public ListIterator listIterator()
作用:返回從頭結點開始迭代的迭代器
原始碼如下:
//inherited from AbstractList
public ListIterator<E> listIterator() {
return listIterator(0);//返回從指定節點(頭結點)開始的迭代器,listIterator(final int index)原始碼詳見上述:public ListIterator listIterator(final int index)
}
原始碼分析:
listIterator()方法底層呼叫listIterator(final int index)實現,返回從頭結點開始進行迭代的迭代器public Iterator descendingIterator()
作用:返回逆序的元素的迭代器
原始碼分析:
public Iterator<E> descendingIterator() {
return new DescendingIterator();
}
private class DescendingIterator implements Iterator<E> {
private final ListItr itr = new ListItr(size());//獲取ListItr物件
public boolean hasNext() {//進行逆向迭代
return itr.hasPrevious();
}
public E next() {//逆向迭代
return itr.previous();
}
public void remove() {
itr.remove();
}
}
原始碼分析:
從類名和上面的程式碼可以看出這是一個反向的Iterator,程式碼很簡單,都是呼叫的ListItr類中的方法。public Object remove(int index)
作用:移除指定位置的元素。
原始碼如下:
public E remove(int index) {
checkElementIndex(index);//檢查索引是否合法
return unlink(node(index));//移除索引處的節點
}
原始碼分析:
checkElementIndex(),原始碼詳見上述:public Object get(int index)
node(),原始碼詳見上述:public boolean addAll(Collection c)
unlink(),原始碼詳見上述:public boolean remove(Object o)
public boolean removeAll(Collection c)
作用:移除指定集合在此列表中的所有元素
原始碼如下:
//inherited from AbstractCollection
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);//判斷要移除的集合是否為空
return batchRemove(c, false);
}
/*
@parameter:Collection c:目標集合
boolean complement:當前集合是否需要包含目標集合
false,則刪除目標集合在當前集合中所存在的元素
true,則刪除目標集合在當前集合中不純在的元素
*/
private boolean batchRemove(Collection<?> c, boolean complement)//
final Object[] elementData = this.elementData;//定義當前陣列物件
int r = 0, w = 0;
// r 在外層定義for迴圈所用下標,目的是為了判斷有沒有報錯,如果報錯了,r就!=size,如果沒有報錯r==size
// w 進行操作的次數
boolean modified = false;//定義成功標識
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)//目標集合中元素存在或不存在 在當前集合中
elementData[w++] = elementData[r];//將不需要移除的元素放入elementData陣列中
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {//表示報錯了,與ArrayList的父類保持一致,當c.contains()這個方法丟擲異常才會r != size
System.arraycopy(elementData, r,
elementData, w,
size - r);//複製陣列,從報錯下標開始複製到 w下標(最後被修改的下標),複製長度是未成功迴圈的長度
w += size - r;//因為複製後陣列長度就變了,所以需要求出目前陣列的長度,w+ 複製的長度
}
if (w != size) {// 表示原陣列有刪除了資料
// clear to let GC do its work
for (int i = w; i < size; i++)
//從w開始迴圈,迴圈到size,這些資料是要刪除的資料,設為null
elementData[i] = null;
modCount += size - w;//修改操作次數計數 size-w表示刪除的數量
size = w; //將長度設為新的長度
modified = true;
}
}
return modified;
}
原始碼分析:
1.判斷要移除的集合是否為null,如果為空則丟擲null異常
2.如果不為null,則呼叫batchRemove方法即可移除成功
2.1. 通過contains方法,將所有不需要移除的元素放入elementData陣列中,然後標記最後一個放入元素的位置w
2.2. 將所有需要移除的元素設定為null
2.3. 將size的大小設定為w
2.4. 修改modCount欄位(該阻斷主要是為了防止多執行緒模式下安全問題)
2.5. 返回移除成功的標識
public boolean retainAll(Collection c)
作用:原集合和引數集合做交集,保留相同的部分。
原始碼如下:
//inherited from AbstractCollection
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);//判斷要移除的集合是否為空
boolean modified = false;//判斷連結串列是否被修改
Iterator<E> it = iterator();
while (it.hasNext()) {//遍歷連結串列
//如果c中不包含原集合某元素,則在元集合中刪除此元素
if (!c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
原始碼分析:
使用迭代器遍歷LinkedList集合,比較是否含有c中的元素,將LinkedList集合中與c中元素不等的元素移除。public Object set(int index,Object element)
作用:修改LinkedList連結串列指定位置的值,並返回原值
原始碼如下:
public E set(int index, E element) {
checkElementIndex(index);//檢查索引是否合法
Node<E> x = node(index);//Node型別變數x指向index所在的節點
E oldVal = x.item;//將index位置節點的元素賦值給oldVal
x.item = element;//用element覆蓋初值
return oldVal;
}
public List subList(int fromIndex,int toIndex)
作用:只返回包含從fromIndex到toIndex之間的資料元素的連結串列。
原始碼如下:
//inherited from AbstractList
public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<E>(this, fromIndex, toIndex) :
new SubList<E>(this, fromIndex, toIndex));
}
原始碼分析:
這裡的this是多型的,指的是那個外部呼叫subList方法的那個List物件,先判斷this是否實現了RandomAccess介面,實現返回RandomAccessSublist,否則返回SubList,通過構造方法將this傳給SubList中屬性。一.若外部呼叫subList方法的那個List物件實現了RandomAccess介面
RandomAccess介面原始碼如下:
public interface RandomAccess {
}
原始碼分析:
RandomAccess定義為空介面作為標識介面,實現了RandomAccess介面的物件(? instance RandomAccess)返回1。RandomAccessSublist原始碼如下所示:
//inherited from AbstractList
class RandomAccessSubList<E> extends SubList<E> implements RandomAccess {
RandomAccessSubList(AbstractList<E> list, int fromIndex, int toIndex) {
super(list, fromIndex, toIndex);
}
public List<E> subList(int fromIndex, int toIndex) {
return new RandomAccessSubList<>(this, fromIndex, toIndex);
}
}
原始碼分析:
RandomAccessSubList中的構造方法呼叫了父類SubList類的構造方法subList構造方法原始碼(部分)如下:
//from AbstractList
class SubList<E> extends AbstractList<E> {
private final AbstractList<E> l;
private final int offset;
private int size;
SubList(AbstractList<E> list, int fromIndex, int toIndex) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > list.size())
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
l = list;
offset = fromIndex;
size = toIndex - fromIndex;
this.modCount = l.modCount;
}
…………………//(其他方法)
}
通過對offset與size的限定,給呼叫subList方法的物件返回集合中的部分檢視
二.若外部呼叫subList方法的那個List物件未實現了RandomAccess介面,則直接返回SubList,原始碼同上。
public Object[] toArray()
作用:將當前集合中的所有元素轉化成頂級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;
}
原始碼分析:
1.建立一個與LinkedList連結串列等大的陣列。
2.通過遍歷LinkedList連結串列,將連結串列元素新增到陣列中。
public Object[] toArray(Object []a)
作用:將集合中的元素依次存入引數陣列中並返回。
原始碼如下:
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
if (a.length > size)
a[size] = null;
return a;
}
原始碼分析:
猜想:通過反射機制將連結串列的值賦值到a陣列中。
實現Deque介面方法
public void addFirst(Object element)
作用:將指定元素查到此列表的開頭
原始碼如下:
public void addFirst(E e) {
linkFirst(e);//呼叫linkFirst將元素e插入到連結串列的表頭
}
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
原始碼分析:
linkFirst()的實現分為兩種情況:1.LinkedList連結串列為空;2.LinkedList連結串列非空1.當LinkedList連結串列為空呼叫linkFirst(),如下圖所示:
2.當LinkedList連結串列非空呼叫linkFirst(),如下圖所示:
public void addLast(Object element)
作用:在LinkedList尾部新增一個新元素。
原始碼如下:
public void addLast(E e) {
linkLast(e);
}
原始碼分析:
底層呼叫了linkLast方法,linkLast方法詳見上述:public boolean add(Object o);public Object getFirst()
作用:返回LinkedList連結串列頭結點的元素
原始碼如下:
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
原始碼分析:
當頭結點為空時,丟擲異常;否則,將頭結點元素返回。public Object getLast()
作用:返回LinkedList連結串列尾結點的元素
原始碼如下:
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
原始碼分析:
當尾結點為空時,丟擲異常;否則,將尾結點元素返回。public Object removeFirst()
作用:移除LinkedList頭結點.
原始碼如下:
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;
}
原始碼分析:
當頭結點為空時,丟擲異常。否則,呼叫unlinkFirst()移除頭結點。removeFirst()執行過程如下圖所示:
public Object removeLast()
作用:移除LinkedList連結串列尾結點。
原始碼如下:
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;
}
原始碼分析:
當尾結點為空時,丟擲異常。否則,呼叫unlinkLast()移除頭結點。removeLast()執行過程如下圖所示:
其他方法
public Object clone()
作用:此方法用於建立連結串列的重複副本和淺表副本(淺複製)
原始碼如下:
//淺克隆(元素不會被克隆);return一個副本
public Object clone() {
LinkedList<E> clone = superClone();//呼叫父類(Object)的克隆方法
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0;
for (Node<E> x = first; x != null; x = x.next)
clone.add(x.item);
return clone;
}
private LinkedList<E> superClone() {
try {
return (LinkedList<E>) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
原始碼解析:
首先獲取從輔助方法返回的LinkedList物件,接著把該物件的所有域都設定為初始值。然後把LinkedList中所有的內容複製到返回的物件中。public Iterator iterator()
作用:返回當前集合的雙向迭代器。
原始碼如下:
//inherited from AbstractList
public Iterator<E> iterator() {
return new Itr();
}
返回的是一個Itr類的物件,接下來我們來看它的原始碼
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
原始碼分析:
每個實現Iterable介面的類必須提供一個iterator方法,返回一個Iterator物件,LinkedList也不例外。cursor--;
expectedModCount = modCount;
如圖所示,Cursor初始狀態是指向5,即若呼叫ArrayList的迭代器使用next()遍歷陣列,在遍歷途中使用ArrayList.remove(1),則會跳原本應該遍歷的5,直接遍歷到6.採用上述程式碼後,它在每一次刪除之後都會將cursor(下一項)的位置設定為當前位置,也就是將cursor往前移動了一位,之後再將modCount賦值給expectedModCount使它們保持相等。
public String toString()
作用:一個arraylist轉換為字串
原始碼如下:
//inherited from AbstractCollection
public String toString() {
Iterator<E> it = iterator();//集合本身呼叫迭代器方法(this.iterator),得到集合迭代器
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
原始碼分析:
若集合沒有元素->返回String型別"[]" 若集合有元素->先拼接'['->進入for死迴圈用append拼接e元素+','+' '->沒有元素使用return跳出死迴圈並拼接']'六.參考資料
LinkedList原始碼解析 基於Node結構
【Java集合】LinkedList詳解前篇
JavaSE基礎知識(二十一)--Java集合(容器)之類LinkedList的內部類ListItr的原始碼分析
LinkedList原始碼解析(基於JDK1.8)