轉自:http://www.cnblogs.com/huangfox/archive/2010/10/11/1847863.htmlhtml
1、 LinkedList
安全
3.1 建立:LinkedList()數據結構
LinkedList底層的數據結構是一個雙向鏈表。既然是雙向鏈表,那麼一定存在一種數據結構——咱們能夠稱之爲節點,節點實例保存業務數據,前一個節點的位置信息和後一個節點位置信息,以下圖所示:函數
圖——雙線鏈表及節點示意圖this
首先來了解節點類:spa
private static class Entry{線程
Eelement;指針
Entry next;htm
Entry previous;對象
Entry(Eelement, Entrynext, Entryprevious) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
節點類很簡單,element存放業務數據,previous與next分別存放先後節點的信息(在數據結構中咱們一般稱之爲先後節點的指針)。
聲明LinkedList對象時,建立一個Entry對象,不過所有爲空。
private transient Entry header = new Entry(null, null, null);
private transient int size = 0;
在執行構造函數的時候,將header實例的previous和next所有指向header實例。
public LinkedList(){
header.next = header.previous = header;
}
執行完構造函數後,header實例自身造成一個閉環,以下圖所示:
圖——初始化LinkedList
3.2 添加數據:add()
從源代碼中咱們可知——給LinkedList添加數據是從雙向鏈表的頭開始的,代碼以下所示:
public boolean add(E e) {
addBefore(e, header);
return true;
}
private Entry addBefore(Ee, Entry entry) {
Entry newEntry = new Entry(e, entry, entry.previous);
newEntry.previous.next =newEntry;
newEntry.next.previous =newEntry;
size++;
modCount++;
return newEntry;
}
下面分解「添加第一個數據」的步驟:
第一步:初始化後LinkedList實例的狀況:
圖——初始化後
第二步:初始化一個預添加的Entry實例(newEntry)。
Entry newEntry = newEntry(e, entry, entry.previous);
圖——建立新節點實例
第三步:調整新加入節點和頭結點(header)的先後指針。
newEntry.previous.next = newEntry;
newEntry.previous即header,newEntry.previous.next即header的next指向newEntry實例。在上圖中應該是「4號線」指向newEntry。
newEntry.next.previous = newEntry;
newEntry.next即header,newEntry.next.previous即header的previous指向newEntry實例。在上圖中應該是「3號線」指向newEntry。
調整後以下圖所示:
圖——加入第一個節點後LinkedList示意圖
下面分解「添加第二個數據」的步驟:
第一步:新建節點。
圖——添加第二個節點
第二步:調整新節點和頭結點的先後指針信息。
圖——調整先後指針信息
添加後續數據狀況和上述一致,LinkedList實例是沒有容量限制的。
3.3 刪除數據:remove()、remove(int)、remove(Object)
LinkedList的數據刪除操做雖然有多種(這裏認爲只包括remove()、remove(int)、remove(Object) ),可是真正執行刪除操做的代碼以下所示:
private E remove(Entry e) {
if (e == header)
throw new NoSuchElementException();
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
return result;
}
Remove方法接收的參數是一個節點實例,即預被刪除的節點。只不過remove()是刪除的第一個節點(頭結點後的第一個節點)——
public EremoveFirst() {
return remove(header.next);
}
Remove(int)是刪除指定位置的節點——
private Entry entry(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry e = header;
if(index < (size >> 1)) {//判斷是從頭開始遍歷仍是從尾開始遍歷
for (int i = 0; i<= index; i++)
e = e.next;
} else {
for (int i = size;i > index; i--)
e = e.previous;
}
return e;
}
Remove(Object)是刪除指定內容的節點——
public boolean remove(Object o) {
if (o==null) {
for (Entry e = header.next; e != header; e = e.next) {
if (e.element==null) {
remove(e);
return true;
}
}
} else {
for (Entry e = header.next; e != header; e = e.next) {
if (o.equals(e.element)) {
remove(e);
return true;
}
}
}
return false;
}
在回到咱們的remove(Entry e)方法:
因爲刪除了某一節點所以調整相應節點的先後指針信息,以下:
e.previous.next = e.next;//預刪除節點的前一節點的後指針指向預刪除節點的後一個節點。
e.next.previous = e.previous;//預刪除節點的後一節點的前指針指向預刪除節點的前一個節點。
清空預刪除節點:
e.next = e.previous = null;
e.element = null;
交給gc完成資源回收,刪除操做結束。
與ArrayList比較而言,LinkedList的刪除動做不須要「移動」不少數據,從而效率更高。
3.4 獲取數據:get(int)
Get(int)方法的實如今remove(int)中已經涉及過了。首先判斷位置信息是否合法(大於等於0,小於當前LinkedList實例的Size),而後遍歷到具體位置,得到節點的業務數據(element)並返回。
注意:爲了提升效率,須要根據獲取的位置判斷是從頭仍是從尾開始遍歷。
private Entry entry(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry e = header;
if(index < (size >> 1)) {//判斷是從頭開始遍歷仍是從尾開始遍歷
for (int i = 0; i<= index; i++)
e = e.next;
} else {
for (int i = size;i > index; i--)
e = e.previous;
}
return e;
}
注意細節:位運算與直接作除法的區別。
3.5 遍歷數據:Iterator()
對ArrayList的iterator有所瞭解後,在此重點關注如下幾點:
當調用iterator方法是,每次都建立一個ListItr實例,它擁有一個屬性cursor,起到遊標的做用。
Iterator實例的hasNext方法:
public boolean hasNext() {
return cursor != size();
}
當遊標跑到最後時返回false,說明遍歷完成。
Iterator實例的next方法:
public E next() {
checkForComodification();
try {
Enext = get(cursor);
lastRet= cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw newNoSuchElementException();
}
}
經過get方法返回當前遊標位置的節點內容,並將遊標後移一個位置。
LinkedList在遍歷的過程當中,若是有添加、刪除等動做發生,會致使ConcurrentModificationException異常,和ArrayList相似。
3.6 判斷存在或獲取位置
獲取位置的IndexOf方法在remove(Object)中已經涉及,而判斷存在的contains(Object)方法則主要是調用IndexOf方法,根據IndexOf返回的位置和-1進行比較進而判斷指定數據是否存在。
3.7 注意
LinkedList是無容量限制的;
LinkedList是非線程安全的;
LinkedList是基於雙向鏈表實現的,當數據順序無關的狀況下,選擇ArrayList仍是LinkedList要從各動做的執行效率綜合考慮。