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()
- 其他方法
-
5.1 共有方法
一.數據結構
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)