標籤:des style blog http color java 使用 os
//-----------------------------------------------------------轉載請註明出處:http://blog.csdn.net/chdjjby Rowandjj2014/8/8//----------------------------------------------------------
註:以下源碼基於jdk1.7.0_11
上一篇我們分析了ArrayList,今天我們再來看下LinkedList。
首先上一幅架構圖:
LinkedList同樣間接繼承了AbstractList抽象類別,對外來看,LinkedList提供的操作介面跟ArrayList是很類似的,
差別在於內部實現上。稍微有點基礎的都知道,LinkedList是
基於雙向鏈表這種資料結構,而ArrayList上一篇已經分析過了,是通過數組實現的。我們依舊按照之前的思路,自頂向下分析,AbstractList以及其上面的類或介面我們上一篇已經分析過,這裡不再重複,我們從AbstractSequentialList開始。
package java.util;public abstract class AbstractSequentialList<E> extends AbstractList<E> { protected AbstractSequentialList() {//只有一個構造器 } public E get(int index) {//擷取指定位置的值 try { return listIterator(index).next();//通過迭代器的方式 } catch (NoSuchElementException exc) {//找不到就拋出異常 throw new IndexOutOfBoundsException("Index: "+index);//這是個Runtime異常 } } public E set(int index, E element) { try { ListIterator<E> e = listIterator(index);//同樣調用的listiterator E oldVal = e.next();//記錄 e.set(element); return oldVal;//返回 } catch (NoSuchElementException exc) { throw new IndexOutOfBoundsException("Index: "+index); } } public void add(int index, E element) { try { listIterator(index).add(element); } catch (NoSuchElementException exc) { throw new IndexOutOfBoundsException("Index: "+index); } } public E remove(int index) { try { ListIterator<E> e = listIterator(index); E outCast = e.next(); e.remove(); return outCast; } catch (NoSuchElementException exc) { throw new IndexOutOfBoundsException("Index: "+index); } } // Bulk Operations public boolean addAll(int index, Collection<? extends E> c) { try { boolean modified = false; ListIterator<E> e1 = listIterator(index); Iterator<? extends E> e2 = c.iterator(); while (e2.hasNext()) { e1.add(e2.next()); modified = true; } return modified; } catch (NoSuchElementException exc) { throw new IndexOutOfBoundsException("Index: "+index); } } // Iterators public Iterator<E> iterator() { return listIterator(); } public abstract ListIterator<E> listIterator(int index);//參數為索引位置。表示從哪開始遍曆}可以發現,這個抽象類別中的方法都依賴於這個ListIterator迭代器,而這個擷取迭代器的方法是抽象的,留給子類完成,另外iterator方法並沒有返回iterator,而同樣是返回了listiterator對象。
接下來,我們分析LinkedList。先看聲明:
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
需要注意的是LinkedList實現了Deque介面,這個介面代表一個雙端隊列,內部封裝了雙端隊列的所有操作,故而
LinkedList可以當做一個棧、隊列或者是雙端隊列來使用。下面是其成員變數:
transient int size = 0;//集合大小(結點個數) transient Node<E> first;//頭指標 transient Node<E> last;//尾指標
前面說過,linkedList是通過雙向鏈表實現,故而不需要有擴容的方法,因為結點是動態申請的。而這個結點的類型即為Node。下面看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; } }很顯然是個
雙向鏈表的結點結構。再看LinkedList構造器:
public LinkedList() {} public LinkedList(Collection<? extends E> c) { this(); addAll(c); }再看一些對結點的操作方法:如果你熟悉雙向鏈表,就會發現下面幾個函數很簡單,無非是處理指標的指向問題。
private void linkFirst(E e) {//插到頭部 final Node<E> f = first; //建立一個新結點,前驅為空白,後繼為f(也就是當前的頭結點) final Node<E> newNode = new Node<>(null, e, f);//注意這種泛型的寫法也是可以的 first = newNode;//頭指標指向新結點 if (f == null)//鏈表為空白時 last = newNode;//尾指標指向新結點 else//否則 f.prev = newNode;//讓f的前驅指向新結點 size++; modCount++; } void linkLast(E e) {//插到尾部 final Node<E> l = last;//臨時變數記錄尾結點 final Node<E> newNode = new Node<>(l, e, null);//建立新結點,前驅為l last = newNode;//更新尾指標 if (l == null)//如果鏈表為空白 first = newNode; else l.next = newNode; size++; modCount++;//用於快速失敗機制 } void linkBefore(E e, Node<E> succ) {//將e插入succ之前 // assert succ != null;//調用者需要保證succ不為空白 final Node<E> pred = succ.prev;//記錄succ的前驅 final Node<E> newNode = new Node<>(pred, e, succ);//新結點的前驅指向succ的前驅,新結點的後繼指向succ succ.prev = newNode;//succ的前驅指向新結點 if (pred == null)//succ為頭結點 first = newNode;//更改頭指標 else pred.next = newNode;//否則succ的前驅的後繼指向新結點 size++; modCount++; } private E unlinkFirst(Node<E> f) {//幹迴轉結點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; } private E unlinkLast(Node<E> l) {//幹掉尾結點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; } E unlink(Node<E> x) {//幹掉一個普通結點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; }有了這些基本函數之後,實現其他動作就方便了。比如這些:
public void addFirst(E e) { linkFirst(e); } public void addLast(E e) { linkLast(e); } public boolean add(E e) { linkLast(e); return true; }再看這個remove方法:
public boolean remove(Object o) { 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; }
跟ArrayList類似,根據參數是否為null,進行了兩種處理,
說明LinkedList也是支援null的元素的。
再看清空操作:
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; modCount++; }下面的函數封裝了索引鏈表位置的操作:
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; } }這個函數用了一個小技巧,首先判斷待索引的位置是在鏈表前半部分還是後半部分,若是前半部分,則順序索引,否則逆序索引(這就是雙向鏈表的優點之一)。之前在AbstractSequentialList中未實現的方法在這裡得到了實現:
public ListIterator<E> listIterator(int index) { checkPositionIndex(index); return new ListItr(index); }
這個ListItr是LinkedList的內部類,實現了ListIterator介面。
private class ListItr implements ListIterator<E> private Node<E> next; ListItr(int index) { // assert isPositionIndex(index); next = (index == size) ? null : node(index); nextIndex = index; }
通過構造器指定起始遍曆的位置,
內部通過調用node方法索引該位置的對象。具體方法限於篇幅不在介紹。值得一提的是這個類還提供了一個反向的迭代器:
public Iterator<E> descendingIterator() { return new DescendingIterator(); }
這個反向迭代器其實是對上面介紹的ListItr的封裝。
總結:1.LinkedList內部通過雙向鏈表實現;2.LinkedList支援null元素;3.LinkedList插入刪除元素較方便,但是尋找操作較耗時(對比ArrayList),雖然內部進行了最佳化(根據位置選擇順序還是逆序遍曆);4.LinkedList內部同樣通過內部類的形式實現了迭代器(僅實現了ListIterator,iterator方法返回的也是ListIterator對象)。5.LinkedList實現了Deque介面,可以當成棧、隊列、雙端隊列來使用。