以下內容基於jdk1.7.0_79源碼;
List接口的鏈表實現,並提供了一些隊列,棧,雙端隊列操作的方法;
與ArrayList對比,LinkedList插入和刪除操作更加高效,隨機訪問速度慢;
可以作為棧、隊列、雙端隊列數據結構使用;
非同步,線程不安全;
與ArrayList、Vector一樣,LinkedList的內部迭代器存在“快速失敗行為”;
支持null元素、有順序、元素可以重復;

以上接口和類中,關於Iterable接口、Collection接口、List接口、 Cloneable、 java.io.Serializable接口、AbstractCollection類、AbstractList類的相關說明,在介紹ArrayList的時候,已經有了個大概說明,這裡將主要了解下Queue接口、Deque接口、AbstractSequentialList類以及LinkedList類;
隊列接口,定義了一些隊列的基本操作,
注意使用時優先選擇以下藍色字體方法(offer、poll、peek),隊列通常不允許null元素,因為我們通常調用poll方法是否返回null來判斷隊列是否為空,但是LinkedList是允許null元素的,所以,在使用LinkedList最為隊列的實現時,不應該將null元素插入隊列;
boolean add(E e);
將對象e插入隊列尾部,成功返回true,失敗(沒有空間)拋出異常IllegalStateException;
boolean offer(E e);
將對象e插入隊列尾部,成功返回true,失敗(沒有空間)返回false;
E remove();
獲取並移除隊列頭部元素,如果隊列為空,拋出NoSuchElementException異常;
E poll();
獲取並移除隊列頭部元素,如果隊列為空,返回null;
E element();
獲取但不移除隊列頭部元素,如果隊列為空,拋出NoSuchElementException異常;
E peek();
獲取但不移除隊列頭部元素,如果隊列為空,返回null;
舉個簡單的例子,基於LinkedList實現的隊列,代碼如下:
package com.pichen.basis.col;
import java.util.LinkedList;
import java.util.Queue;
public class LinkListTest {
public static void main(String[] args) {
Queue<Integer> linkedListQueue = new LinkedList<Integer>();
//入隊
linkedListQueue.offer(3);
linkedListQueue.offer(4);
linkedListQueue.offer(2);
linkedListQueue.offer(1);
//出隊
Integer tmp;
while((tmp = linkedListQueue.poll()) != null){
System.out.println(tmp);
}
System.out.println(linkedListQueue.peek());
}
}
雙端隊列接口,繼承隊列接口,支持在隊列兩端進行入隊和出隊操作;
除了Collection接口Queue接口中定義的方法外,Deque還包括以下方法
void addFirst(E e);
將對象e插入到雙端隊列頭部,容間不足時,拋出IllegalStateException異常;
void addLast(E e);
將對象e插入到雙端隊列尾部,容間不足時,拋出IllegalStateException異常;
boolean offerFirst(E e);
將對象e插入到雙端隊列頭部
boolean offerLast(E e);
將對象e插入到雙端隊列尾部;
E removeFirst();
獲取並移除隊列第一個元素,隊列為空,拋出NoSuchElementException異常;
E removeLast();
獲取並移除隊列最後一個元素,隊列為空,拋出NoSuchElementException異常;
E pollFirst();
獲取並移除隊列第一個元素,隊列為空,返回null;
E pollLast();
獲取並移除隊列最後一個元素,隊列為空,返回null;
E getFirst();
獲取隊列第一個元素,但不移除,隊列為空,拋出NoSuchElementException異常;
E getLast();
獲取隊列最後一個元素,但不移除,隊列為空,拋出NoSuchElementException異常;
E peekFirst();
獲取隊列第一個元素,隊列為空,返回null;
E peekLast();
獲取隊列最後一個元素,隊列為空,返回null;
boolean removeFirstOccurrence(Object o);
移除第一個滿足 (o==null ? e==null : o.equals(e)) 的元素
boolean removeLastOccurrence(Object o);
移除最後一個滿足 (o==null ? e==null : o.equals(e)) 的元素
void push(E e);
將對象e插入到雙端隊列頭部;
E pop();
移除並返回雙端隊列的第一個元素
Iterator<E> descendingIterator();
雙端隊列尾部到頭部的一個迭代器;
一個抽象類,基於迭代器實現數據的隨機訪問,以下方法的含義, 之前也說過,簡單地說,就是數據的隨機存取(利用了一個索引index);
public E get(int index)
public E set(int index, E element)
public void add(int index, E element)
public E remove(int index)
public boolean addAll(int index, Collection<? extends E> c)
LinkedList的具體實現,
LinkedList中有兩個關鍵成員屬性,隊頭結點和隊尾結點:
transient Node<E> first; //隊頭節點 transient Node<E> last; //隊尾節點
LinkedList的節點內部類
具體代碼如下,每個節點包含上一個節點的引用、下一個節點的引用以及該節點引用的具體對象;
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提供的每個方法的含義,在前面隊列、雙端隊列、集合等接口中都有說明了,這裡簡單的舉一兩個方法的具體實現,對照源碼了解下,其實就是鏈表的操作:
poll方法,出隊操作
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
/**
* Unlinks non-null first node 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方法內,首先直接獲取被刪節點,臨時存儲其具體引用的對象element和下個引用next,然後將被刪節點對象引用和下個節點引用置null給gc回收,改變雙端隊列隊頭結點為被刪節點的下個引用next,如果next為空,將雙端隊列的隊尾結點last置null,否則將next節點的前引用置null;隊列長度減減,操作次數加加(快速失敗機制),返回被刪節點引用的具體對象;
public E get(int index)方法,隨機訪問方法
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
/**
* Returns the (non-null) Node at the specified element index.
*/
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;
}
}
LinkedList隨機訪問性能較差,首先是判斷索引index合法性,然後調用node(int index)方法,在node方法中,做了一點優化,先判斷要訪問節點的索引是在隊列的前半部分還是後半部分,如果在前半部分則從隊頭開始遍歷,否則從隊尾開始遍歷,如上代碼所示。
適用場合很重要,注意和ArrayList區分開來,根據集合的各自特點以及具體場景,選擇合適的List實現;
這裡舉個簡單例子,分別使用ArrayList和LinkedList,測試下兩個集合隨機訪問的性能,可以發現使用LinkedList隨機訪問的效率較ArrayList差很多;
package com.pichen.basis.col;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Stack;
import java.util.Vector;
public class Test {
public static void main(String[] args) {
//初始化linkedList和arrayList數據
LinkedList<Integer> linkedList = new LinkedList<Integer>();
for(int i = 0; i < 10000; i++){
linkedList.offerLast(i);
}
List<Integer> arrayList = new ArrayList<Integer>();
for(int i = 0; i < 10000; i++){
arrayList.add(i);
}
long s, e;
s = System.currentTimeMillis();
for(int i = 0; i < 10000; i++){
linkedList.get(i);
}
e = System.currentTimeMillis();
System.out.println("linkedList:" + (e - s) + "ms");
s = System.currentTimeMillis();
for(int i = 0; i < 10000; i++){
arrayList.get(i);
}
e = System.currentTimeMillis();
System.out.println("arrayList:" + (e - s) + "ms");
}
}
結果打印:
linkedList:56ms arrayList:1ms