在上篇文章Java集合(三) ArrayList詳解的學習和原始碼分析中,我們知道ArrayList是以陣列實現,它的優勢是查詢效能高,劣勢是按順序增刪效能差。如果在不確定元素數量的情況時,不建議使用ArrayList。其實當這種情況時,我們就可以使用LinkedList了。
大O記號
一直以來我們用快慢來描述儲存演算法,這種直觀的描述是不好的。有一種方法可以量化一種操作的耗時情況。我們可以借用數學分析中的大O記號來描述這種關係。
解決一個規模為n的問題所花費的步驟如果是n的常數倍,我們就記這個方法的複雜度為O(n)。例如,給一個陣列,求這個陣列中的最大值,需要遍歷這個陣列,如果陣列長度為n,那麼遍歷陣列的步驟就是n。所以,這是一種O(n)的操作。同樣,解決一個規模為n的問題所花費的步驟如果是n^2的常數倍,我們就記這個方法的複雜度為O(n^2)。比如氣泡排序。而解決問題的步驟如果與問題規模無關,這個時間複雜度就是O(1)的。比如,陣列的隨機存取。而陣列的順序儲存就是O(n)的。
連結串列
在學習LinkedList之前,我們先對資料結構中連結串列做一個簡單的回顧。連結串列是一種物理儲存單元上非連續、非順序的儲存結構,資料元素的邏輯順序是通過連結串列中的指標連結次序實現的。連結串列由一系列結點(連結串列中每一個元素成為結點)組成,結點可以在執行時動態生成。
可以這樣理解:
有一條街,小明住在街中一角,他有小紅的地址,然後小紅也是住在這條街,他有小花的地址,同樣小花也有別人的地址。某天我想找小紅玩,但是我不知道她住在哪裡,我可以問小明,就知道小紅住在哪裡了。那麼小明小紅小花這些人之間的關係就組成一個連結串列。
- 單連結串列:就是小明只是右手握著小紅的地址,他只有小紅一個人的地址
- 雙連結串列:就是小紅左手握著小明的地址,右手握著小花的地址,她有兩個人的地址
- 迴圈連結串列:就是小明握著小紅的地址,小紅握著小花的地址,而小花又握著小明的地址,這樣就形成了一個迴圈
- 有序連結串列:以某個標準,給連結串列的元素排序,比如比較內容大小、比較雜湊值等 如果還不好理解,我們可以先看一個類的定義:
public class LinkNode {
public int data;
public LinkNode next;
public LinkNode(int data) {
this.data = data;
this.next = null;
}
}
複製程式碼
我們定義了一個LinkNode類,這個類有兩個成員變數,一個是整形data,還有一個是指向LinkNode型別的引用。通過這個引用,就可以把多個LinkNode串起來,比如,下面的程式碼:
LinkNode head = new LinkNode(1);
head.next = new LinkNode(2);
複製程式碼
這樣就在記憶體裡建立了兩個LinkNode物件,它們之間的關係就像這樣:
head所引用的那個LinkNode,其data為1,其next指向下一個LinkNode,其data為2,next域為空。連結串列增加一項
如果我想在head之後,增加一個連結串列項,讓這個連結串列變成下圖的樣子,該怎麼做?
思路就是先把新的結點的next設為head的next,然後再使得head.next指向新的結點即可。 LinkNode newNode = new LinkNode(3);
newNode.next = head.next;
head.next = newNode;
複製程式碼
反過來,想要把head後面的那個結點刪掉,也很簡單,只需要讓head的next再往後指一項,把原來head後面的那一項跳過去就可以了:
head.next = head.next.next;
複製程式碼
可以看到,在連結串列中,指定的結點後面新增一個項是常數時間的,也就是O(1),刪除一個項也是一樣的。但與陣列不同的,如果我要完成“查詢連結串列中的第n項是什麼”這個操作,就會變成O(n)的,因為我們每次查詢都必須從頭開始,依次向後查詢。
public static int queryData(LinkNode head, int index) {
LinkNode cur = head;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
return cur.data;
}
複製程式碼
再看一個問題,假如我知道了一個LinkNode,不妨記為temp,位於一個連結串列中,如果想知道這個結點的上一個結點是什麼,應該怎麼辦?對於我們前邊定義的連結串列結構,只能從頭開始找,去判斷是否有某個結點,它的next與temp相等。如果相等就說明,這個結點就是temp的前序結點。程式碼如下:
public static LinkNode findPrevious(LinkNode head, LinkNode temp) {
LinkNode cur = head;
while (cur != null) {
if (cur.next == temp)
return cur;
cur = cur.next;
}
return null;
}
複製程式碼
雙向連結串列
實際上,在工程實踐中,使用連結串列的時候,經常會有“查詢某個結點的前一結點”這樣的需求,為了加速這一過程,我們其實可以修改一下LinkNode的定義,結它加上一個指向前序結點的成員變數:
public class DoubleLinkNode {
public int data;
public LinkNode next;
public LinkNode prev;
public DoubleLinkNode(int data) {
this.data = data;
this.next = null;
this.prev = null;
}
}
複製程式碼
這樣的話,連結串列就成了這個樣子了:
這樣一來,查詢某個結點的上一個結點,只要返回它的prev就可以了。好了。連結串列的學習就到這裡了。
LinkedList
概述
LinkedList以雙向連結串列實現。連結串列無容量限制,但雙向連結串列本身使用了更多空間,也需要額外的連結串列指標操作。除了實現List介面外,LinkedList還為在列表的開頭及結尾get、remove和insert元素提供了統一的命名方法。這些操作可以將連結列表當作棧,佇列和雙端佇列來使用。
按索引訪問元素:get(i)/set(i,e)要非常噁心的遍歷連結串列將指標移動到位(如果i > 陣列大小的一半,會從末尾開始移動)。插入、刪除元素時修改前後節點的指標即可,但還是要遍歷部分連結串列的指標才能移動到下標所指的位置,只有在連結串列兩頭的操作:add(),addFirst(),removeLast()或用iterator()上的remove()能省掉指標的移動。
LinkedList同樣是非執行緒安全的,只在單執行緒下適合使用。如果多個執行緒同時訪問一個連結列表,而其中至少一個執行緒從結構上修改了該列表,則它必須保持外部同步。(結構修改指新增或刪除一個或多個元素的任何操作;僅設定元素的值不是結構修改。)這一般通過對自然封裝該列表的物件進行同步操作來完成。如果不存在這樣的物件,則應該使用 Collections.synchronizedList 方法來“包裝”該列表。
LinkedList的iterator和listIterator方法返回的迭代器是快速失敗的(fail-fast機制):在迭代器建立之後,如果從結構上對列表進行修改,除非通過迭代器自身的remove或add方法,其他任何時間任何方式的修改,迭代器都將丟擲 ConcurrentModificationException。
LinkedList實現了Serializable介面,因此它支援序列化,能夠通過序列化傳輸,實現了Cloneable介面,能被克隆。
LinkedList原始碼解析
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable,
java.io.Serializable {
//LinkedList中連結串列元素個數
transient int size = 0;
//連結串列頭結點
transient Node<E> first;
//連結串列尾結點
transient Node<E> last;
//預設構造方法,生成一個空的連結串列
public LinkedList() {
}
//根據c裡面的元素生成一個LinkedList
public LinkedList(Collection<? extends E> c) {
//呼叫空的構造方法
this();
//將c裡面的元素新增到空連結串列尾部
addAll(c);
}
//首部增加結點,結點的值為e
private void linkFirst(E e) {
final Node<E> f = first;//f指向頭結點
//生成一個新結點,結點的值為e,其前向指標為null,後向指標為f
final Node<E> newNode = new Node<>(null, e, f);
//first指向新生成的結點,f儲存著舊的頭結點資訊
first = newNode;
if (f == null)
//如果f為null,則表示整個連結串列目前是空的,則尾結點也指向新結點
last = newNode;
else
//f(老的頭結點)的前向指向最新的結點資訊
f.prev = newNode;
size++;//元素個數+1
modCount++;//修改次數+1
}
//尾部增加結點,結點的值為e
void linkLast(E e) {
final Node<E> l = last; //l指向尾結點
//生成一個新結點,結點的值為e,其前向指標為l,後向指標為null
final Node<E> newNode = new Node<>(l, e, null);
//last指向新生成的結點,l儲存著舊的尾結點資訊
last = newNode;
if (l == null)
//如果l為null,則表示整個連結串列目前是空的,則頭結點也指向新結點
first = newNode;
else
//l(舊的尾結點)的後向指標指向最新的結點資訊
l.next = newNode;
size++;//元素個數+1
modCount++;//修改次數+1
}
//非空結點succ之前插入新結點,新結點的值為e
void linkBefore(E e, Node<E> succ) {
// assert succ != null; //外界呼叫需保證succ不為null,否則程式會丟擲空指標異常
final Node<E> pred = succ.prev;//pred指向succ的前向結點
//生成一個新結點,結點的值為e,其前向指標指向pred,後向指標指向succ
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;//succ的前向指標指向newNode
if (pred == null)
//如果pred為null,則表示succ為頭結點,此時頭結點指向最新生成的結點newNode
first = newNode;
else
//pred的後向指標指向新生成的結點,此時已經完成了結點的插入操作
pred.next = newNode;
size++;//元素個數+1
modCount++;//修改次數+1
}
//刪除頭結點,並返回該結點的值
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;//需確保f為頭結點,且連結串列不為null
final E element = f.item;//獲得結點的值
final Node<E> next = f.next;//next指向f的後向結點
f.item = null;//釋放資料結點
f.next = null; // help GC //釋放f的後向指標
first = next; //first指向f的後向結點
if (next == null)
//如果next為null,則表示f為last結點,此時連結串列即為空連結串列
last = null;
else
//修改next的前向指標,因為first結點的前向指標為null
next.prev = null;
size--; //元素個數-1
modCount++; //修改次數+1
return element;
}
//刪除尾結點,並返回尾結點的內容
private E unlinkLast(Node<E> l) {
// assert l == last && l != null; //需確保l為尾結點,且連結串列不為null
final E element = l.item; //獲得結點的值
final Node<E> prev = l.prev; //prev執行1的前向結點
l.item = null; //釋放l結點的值
l.prev = null; // help GC //釋放l結點的前向指標
last = prev; //last結點指向l的前向結點
if (prev == null)
//如果prev為null,則表示l為first結點,此時連結串列即為空連結串列
first = null;
else
//修改prev的後向指標,因為last結點的後向指標為null
prev.next = null;
size--;//元素個數-1
modCount++;//修改次數+1
return element;
}
//刪除結點x
E unlink(Node<E> x) {
// assert x != null; //需確保x不為null,否則後續操作會丟擲空指標異常
final E element = x.item; //儲存x結點的值
final Node<E> next = x.next;//next指向x的後向結點
final Node<E> prev = x.prev;//prev指向x的前向結點
if (prev == null) {
//如果prev為空,則x結點為first結點,此時first結點指向next結點(x的後向結點)
first = next;
} else {
prev.next = next;//x的前向結點的後向指標指向x的後向結點
x.prev = null; //釋放x的前向指標
}
if (next == null) {
//如果next結點為空,則x結點為尾部結點,此時last結點指向prev結點(x的前向結點)
last = prev;
} else {
next.prev = prev;//x的後向結點的前向指標指向x的前向結點
x.next = null; //釋放x的後向指標
}
x.item = null; //釋放x的值節點,此時x節點可以完全被GC回收
size--; //元素個數-1
modCount++; //修改次數+1
return element;
}
//獲得頭結點的值
public E getFirst() {
final Node<E> f = first;//f指向first結點
if (f == null)//如果連結串列為空
throw new NoSuchElementException();
return f.item;//返回first結點的值
}
//獲得尾結點的值
public E getLast() {
final Node<E> l = last; //l指向last結點
if (l == null)//如果連結串列為空
throw new NoSuchElementException();
return l.item;//返回last結點的值
}
//移除頭結點
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);//摘除尾結點
}
//新增到頭結點,結點的值為e
public void addFirst(E e) {
linkFirst(e);//新增到頭部
}
//新增到尾結點
public void addLast(E e) {
linkLast(e);//新增到尾部
}
//判斷元素(值為o)是否o在連結串列中
public boolean contains(Object o) {
return indexOf(o) != -1;//定位元素
}
//返回元素個數
public int size() {
return size;
}
//新增元素,元素值為e
public boolean add(E e) {
linkLast(e);//新增到連結串列尾部
return true;
}
//移除值為o的元素,o可以為null,找到一個刪除即返回
public boolean remove(Object o) {
if (o == null) {//元素為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;
}
//將c中的元素都新增到當前連結串列中
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);//新增到連結串列尾部
}
//在序號為index處,新增c中所有的元素到當前連結串列中(後向新增)
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//判斷index是否超出界
Object[] a = c.toArray();//將集合轉換為陣列
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;
if (index == size) {//如果index為元素個數,即index個結點為尾結點
succ = null;
pred = last;//指向為結點
} else {
succ = node(index); //succ指向第index個結點
pred = succ.prev; //pred指向succ的前向結點
}
//for迴圈結束後,a裡面的元素都新增到當前連結串列裡面,後向新增
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
//新生成一個結點,結點的前向指標指向pred,後向指標為null
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
//如果pred為null,則succ為當前頭結點
first = newNode;
else
//pred的後向指標指向新結點
pred.next = newNode;
pred = newNode;//pred移動到新結點
}
if (succ == null) {
last = pred;//succ為null,這表示index為尾結點之後
} else {
//pred表示所有元素新增之後的最後結點,此時pred的後向指標指向之前的記錄的結點
pred.next = succ;
succ.prev = pred;//之前記錄的結點指向新增元素之後的最後結點
}
size += numNew;//元素個數+num
modCount++;//修改次數+1
return true;
}
//清除連結串列裡面的所有元素
public void clear() {
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null; //釋放值結點,便於GC回收
x.next = null; //釋放前向指標
x.prev = null; //釋放後向指標
x = next; //後向遍歷
}
first = last = null;//釋放頭尾結點
size = 0;
modCount++;
}
//獲得第index個結點的值
public E get(int index) {
checkElementIndex(index);
return node(index).item; //點位第index結點,返回值資訊
}
//設定第index元素的值
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);//定位第index個結點
E oldVal = x.item;
x.item = element;
return oldVal;
}
//第index個結點之前新增結點
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
//刪除第index個結點
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
//判斷index是否是連結串列中的元素的索引
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
//判斷index是否是連結串列中的元素的索引
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
private String outOfBoundsMsg(int index) {
return "Index: " + index + ", Size: " + size;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//定位連結串列中的第index個結點
Node<E> node(int index) {
// assert isElementIndex(index);//確保是合法的索引,即0<=index<=size
//index小於size的一半時,從頭向後找
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {//index大於size的一半時,從尾向前找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
//定位元素,首次出現的元素的值為o的結點序號
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
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;
}
//定位元素,最後一次出現的元素值為o的元素序號
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;
}
//實現佇列操作,返回第一個元素的值
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//實現佇列操作,返回第一個結點
public E element() {
return getFirst();
}
//實現佇列操作,彈出第一個結點
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//刪除結點
public E remove() {
return removeFirst();
}
//新增結點
public boolean offer(E e) {
return add(e);
}
//新增頭結點
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
//新增尾結點
public boolean offerLast(E e) {
addLast(e);
return true;
}
//返回頭結點的值
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//返回尾結點的值
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
//彈出第一個結點
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//彈出最後一個結點
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
//新增頭部結點
public void push(E e) {
addFirst(e);
}
//彈出第一個結點
public E pop() {
return removeFirst();
}
//刪除值為o的結點
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
//刪除值為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;
}
//返回雙向迭代器
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
//私有內部類,實現雙向迭代器
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned;//記錄當前結點資訊
private Node<E> next;//當前結點的後向結點
private int nextIndex;//當前結點的序號
private int expectedModCount = modCount;//修改次數
//初始化
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
//是否有結點
public boolean hasNext() {
return nextIndex < size;
}
//返回下一個結點
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;//記錄當前結點
next = next.next;//向後移動
nextIndex++;//結點序號+1
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++;
}
//設定當前結點的值
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++;
}
// 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();
}
//判斷迭代期間是否被修改
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
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 Iterator<E> descendingIterator() {
return new DescendingIterator();
}
//前向迭代器
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();
}
}
@SuppressWarnings("unchecked")
private LinkedList<E> superClone() {
try {
return (LinkedList<E>) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
//拷貝操作,執行淺拷貝,只複製引用,而沒有複製引用指向的記憶體
public Object clone() {
LinkedList<E> clone = superClone();
// Put clone into "virgin" state
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0;
// Initialize clone with our elements
for (Node<E> x = first; x != null; x = x.next)
clone.add(x.item);
return clone;
}
//轉換為陣列
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;
}
//轉換為陣列
@SuppressWarnings("unchecked")
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;
}
//序列化版本
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);
}
//反序列化
@SuppressWarnings("unchecked")
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());
}
//獲取一個分割器,1.8新增
@Override
public Spliterator<E> spliterator() {
return new LLSpliterator<E>(this, -1, 0);
}
/**
* A customized variant of Spliterators.IteratorSpliterator
*/
static final class LLSpliterator<E> implements Spliterator<E> {
···
}
}
複製程式碼
總結
關於LinkedList的原始碼,給出幾點比較重要的總結:
1.從原始碼中很明顯可以看出,LinkedList的實現是基於雙向連結串列的,且頭結點中不存放資料。
2.注意兩個不同的構造方法。無參構造方法直接建立一個僅包含head節點的空連結串列;包含Collection的構造方法,先呼叫無參構造方法建立一個空連結串列,而後將Collection中的資料加入到連結串列的尾部後面。
3.在查詢和刪除某元素時,原始碼中都劃分為該元素為null和不為null兩種情況來處理,LinkedList中允許元素為null。
4.LinkedList是基於連結串列實現的,因此不存在容量不足的問題,所以這裡沒有擴容的方法。
5.Node node(int index)方法。該方法返回雙向連結串列中指定位置處的節點,而連結串列中是沒有下標索引的,要指定位置出的元素,就要遍歷該連結串列,從原始碼的實現中,我們看到這裡有一個加速動作。原始碼中先將index與長度size的一半比較,如果index<(size<<1),就只從位置0往後遍歷到位置index處,而如果index>(size<<1),就只從位置size往前遍歷到位置index處。這樣可以減少一部分不必要的遍歷,從而提高一定的效率(實際上效率還是很低)。
6.LinkedList是基於連結串列實現的,因此插入刪除效率高,查詢效率低(雖然有一個加速動作)。
7.要注意原始碼中還實現了棧和佇列的操作方法,因此也可以作為棧、佇列和雙端佇列來使用。