LinkedList也和ArrayList一樣實現了List接口
但是它執行插入和刪除操作時比ArrayList更加高效
因為它是基於鏈表的
基於鏈表也決定了它在隨機訪問方面要比ArrayList遜色一點
除此之外
LinkedList還提供了一些可以使其作為棧
隊列
雙端隊列的方法
這些方法中有些彼此之間只是名稱的區別
以使得這些名字在特定的上下文中顯得更加的合適
先看LinkedList類的定義
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>
Deque<E>
Cloneable
java
io
Serializable
LinkedList繼承自AbstractSequenceList
實現了List及Deque接口
其實AbstractSequenceList已經實現了List接口
這裡標注出List只是更加清晰而已
AbstractSequenceList提供了List接口骨干性的實現以減少實現List接口的復雜度
Deque接口定義了雙端隊列的操作
LinkedList中之定義了兩個屬性
private transient Entry<E> header = new Entry<E>(null
null
null);
private transient int size =
;
size肯定就是LinkedList對象裡面存儲的元素個數了
LinkedList既然是基於鏈表實現的
那麼這個header肯定就是鏈表的頭結點了
Entry就是節點對象了
一下是Entry類的代碼
private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous;
Entry(E element
Entry<E> next
Entry<E> previous) {
this
element = element;
this
next = next;
this
previous = previous;
}
}
只定義了存儲的元素
前一個元素
後一個元素
這就是雙向鏈表的節點的定義
每個節點只知道自己的前一個節點和後一個節點
來看LinkedList的構造方法
public LinkedList() {
header
next = header
previous = header;
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
LinkedList提供了兩個構造方法
第一個構造方法不接受參數
只是將header節點的前一節點和後一節點都設置為自身(注意
這個是一個雙向循環鏈表
如果不是循環鏈表
空鏈表的情況應該是header節點的前一節點和後一節點均為null)
這樣整個鏈表其實就只有header一個節點
用於表示一個空的鏈表
第二個構造方法接收一個Collection參數c
調用第一個構造方法構造一個空的鏈表
之後通過addAll將c中的元素全部添加到鏈表中
來看addAll的內容
public boolean addAll(Collection<? extends E> c) {
return addAll(size
c);
}
// index參數指定collection中插入的第一個元素的位置
public boolean addAll(int index
Collection<? extends E> c) {
// 插入位置超過了鏈表的長度或小於
報IndexOutOfBoundsException異常
if (index <
|| index > size)
throw new IndexOutOfBoundsException(
Index:
+index+
Size:
+size);
Object[] a = c
toArray();
int numNew = a
length;
// 若需要插入的節點個數為
則返回false
表示沒有插入元素
if (numNew==
)
return false;
modCount++;
// 保存index處的節點
插入位置如果是size
則在頭結點前面插入
否則獲取index處的節點
Entry<E> successor = (index==size ? header : entry(index));
// 獲取前一個節點
插入時需要修改這個節點的next引用
Entry<E> predecessor = successor
previous;
// 按順序將a數組中的第一個元素插入到index處
將之後的元素插在這個元素後面
for (int i=
; i<numNew; i++) {
// 結合Entry的構造方法
這條語句是插入操作
相當於C語言中鏈表中插入節點並修改指針
Entry<E> e = new Entry<E>((E)a[i]
successor
predecessor);
// 插入節點後將前一節點的next指向當前節點
相當於修改前一節點的next指針
predecessor
next = e;
// 相當於C語言中成功插入元素後將指針向後移動一個位置以實現循環的功能
predecessor = e;
}
// 插入元素前index處的元素鏈接到插入的Collection的最後一個節點
successor
previous = predecessor;
// 修改size
size += numNew;
return true;
}
構造方法中的調用了addAll(Collection<? extends E> c)方法
而在addAll(Collection<? extends E> c)方法中僅僅是將size當做index參數調用了addAll(int index
Collection<? extends E> c)方法
private Entry<E> entry(int index) {
if (index <
|| index >= size)
throw new IndexOutOfBoundsException(
Index:
+index+
Size:
+size);
Entry<E> e = header;
// 根據這個判斷決定從哪個方向遍歷這個鏈表
if (index < (size >>
)) {
for (int i =
; i <= index; i++)
e = e
next;
} else {
// 可以通過header節點向前遍歷
說明這個一個循環雙向鏈表
header的previous指向鏈表的最後一個節點
這也驗證了構造方法中對於header節點的前後節點均指向自己的解釋
for (int i = size; i > index; i
)
e = e
previous;
}
return e;
}
結合上面代碼中的注釋及雙向循環鏈表的知識
應該很容易理解LinkedList構造方法所涉及的內容
下面開始分析LinkedList的其他方法
add(E e)
public boolean add(E e) {
addBefore(e
header);
return true;
}
從上面的代碼可以看出
add(E e)方法只是調用了addBefore(E e
Entry<E> entry)方法
並且返回true
addBefore(E e
Entry<E> entry)
private Entry<E> addBefore(E e
Entry<E> entry) {
Entry<E> newEntry = new Entry<E>(e
entry
entry
previous);
newEntry
previous
next = newEntry;
newEntry
next
previous = newEntry;
size++;
modCount++;
return newEntry;
}
addBefore(E e
Entry<E> entry)方法是個私有方法
所以無法在外部程序中調用(當然
這是一般情況
你可以通過反射上面的還是能調用到的)
addBefore(E e
Entry<E> entry)先通過Entry的構造方法創建e的節點newEntry(包含了將其下一個節點設置為entry
上一個節點設置為entry
previous的操作
相當於修改newEntry的
指針
)
之後修改插入位置後newEntry的前一節點的next引用和後一節點的previous引用
使鏈表節點間的引用關系保持正確
之後修改和size大小和記錄modCount
然後返回新插入的節點
總結
addBefore(E e
Entry<E> entry)實現在entry之前插入由e構造的新節點
而add(E e)實現在header節點之前插入由e構造的新節點
add(int index
E e)
public void add(int index
E element) {
addBefore(element
(index==size ? header : entry(index)));
}
也是調用了addBefore(E e
Entry<E> entry)方法
只是entry節點由index的值決定
構造方法
addAll(Collection<? extends E> c)
add(E e)
addBefor(E e
Entry<E> entry)方法可以構造鏈表並在指定位置插入節點
為了便於理解
下面給出插入節點的示意圖
addFirst(E e)
public void addFirst(E e) {
addBefore(e
header
next);
}
addLast(E e)
public void addLast(E e) {
addBefore(e
header);
}
看上面的示意圖
結合addBefore(E e
Entry<E> entry)方法
很容易理解addFrist(E e)只需實現在header元素的下一個元素之前插入
即示意圖中的一號之前
addLast(E e)只需在實現在header節點前(因為是循環鏈表
所以header的前一個節點就是鏈表的最後一個節點)插入節點(插入後在
號節點之後)
clear()
public void clear() {
Entry<E> e = header
next;
// e可以理解為一個移動的
指針
因為是循環鏈表
所以回到header的時候說明已經沒有節點了
while (e != header) {
// 保留e的下一個節點的引用
Entry<E> next = e
next;
// 接觸節點e對前後節點的引用
e
next = e
previous = null;
// 將節點e的內容置空
e
element = null;
// 將e移動到下一個節點
e = next;
}
// 將header構造成一個循環鏈表
同構造方法構造一個空的LinkedList
header
next = header
previous = header;
// 修改size
size =
;
modCount++;
}
上面代碼中的注釋已經足以解釋這段代碼的邏輯
需要注意的是提到的
指針
僅僅是概念上的類比
Java並不存在
指針
的概念
而只有引用
為了便於理解所以部分說明使用了
指針
contains(Object o)
public boolean contains(Object o) {
return indexOf(o) !=
;
}
僅僅只是判斷o在鏈表中的索引
先看indexOf(Object o)方法
public int indexOf(Object o) {
int index =
;
if (o==null) {
for (Entry e = header
next; e != header; e = e
next) {
if (e
element==null)
return index;
index++;
}
} else {
for (Entry e = header
next; e != header; e = e
next) {
if (o
equals(e
element))
return index;
index++;
}
}
return
;
}
indexOf(Object o)判斷o鏈表中是否存在節點的element和o相等
若相等則返回該節點在鏈表中的索引位置
若不存在則放回
contains(Object o)方法通過判斷indexOf(Object o)方法返回的值是否是
來判斷鏈表中是否包含對象o
element()
public E element() {
return getFirst();
}
getFirst()
public E getFirst() {
if (size==
)
throw new NoSuchElementException();
return header
next
element;
}
element()方法調用了getFirst()返回鏈表的第一個節點的元素
為什麼要提供功能一樣的兩個方法
像是包裝了一下名字?其實這只是為了在不同的上下文
語境
中能通過更貼切的方法名調用罷了
get(int index)
public E get(int index) {
return entry(index)
element;
}
get(int index)方法用於獲得指定索引位置的節點的元素
它通過entry(int index)方法獲取節點
entry(int index)方法遍歷鏈表並獲取節點
在上面有說明過
不再陳述
set(int index
E element)
public E set(int index
E element) {
Entry<E> e = entry(index);
E oldVal = e
element;
e
element = element;
return oldVal;
}
先獲取指定索引的節點
之後保留原來的元素
然後用element進行替換
之後返回原來的元素
getLast()
public E getLast() {
if (size==
)
throw new NoSuchElementException();
return header
previous
element;
}
getLast()方法和getFirst()方法類似
只是獲取的是header節點的前一個節點的元素
因為是循環鏈表
所以header節點的前一節點就是鏈表的最後一個節點
lastIndexOf(Object o)
public int lastIndexOf(Object o) {
int index = size;
if (o==null) {
for (Entry e = header
previous; e != header; e = e
previous) {
index
;
if (e
element==null)
return index;
}
} else {
for (Entry e = header
previous; e != header; e = e
previous) {
index
;
if (o
equals(e
element))
return index;
}
}
return
;
}
因為查找的是last index
即最後一次出現的位置
所以采用由後向前的遍歷方式
因為采用了有後向前的遍歷
所以index被賦值為size
並且循環體內執行時都進行減操作
分兩種情況判斷是否存在
分別是null和不為空
offer(E e)
public boolean offer(E e) {
return add(e);
}
在鏈表尾部插入元素
offerFirst(E e)
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
在鏈表開頭插入元素
offerLast(E e)
public boolean offerLast(E e) {
addLast(e);
return true;
}
在鏈表末尾插入元素
上面這三個方法都只是調用了相應的add方法
同樣只是提供了不同的方法名在不同的語境下使用
peek()
public E peek() {
if (size==
)
return null;
return getFirst();
}
peekFirst()
public E peekFirst() {
if (size==
)
return null;
return getFirst();
}
peekLast()
public E peekLast() {
if (size==
)
return null;
return getLast();
}
上面的三個方法也很簡單
只是調用了對應的get方法
poll()
public E poll() {
if (size==
)
return null;
return removeFirst();
}
pollFirst()
public E pollFirst() {
if (size==
)
return null;
return removeFirst();
}
pollLast()
public E pollLast() {
if (size==
)
return null;
return removeLast();
}
poll相關的方法都是獲取並移除某個元素
都是和remove操作相關
pop()
public E pop() {
return removeFirst();
}
push(E e)
public void push(E e) {
addFirst(e);
}
這兩個方法對應棧的操作
即彈出一個元素和壓入一個元素
僅僅是調用了removeFirst()和addFirst()方法
下面集中看remove相關操作的方法
remove()
public E remove() {
return removeFirst();
}
remove(int index)
public E remove(int index) {
return remove(entry(index));
}
remove(Object o)
public boolean remove(Object o) {
if (o==null) {
for (Entry<E> e = header
next; e != header; e = e
next) {
if (e
element==null) {
remove(e);
return true;
}
}
} else {
for (Entry<E> e = header
next; e != header; e = e
next) {
if (o
equals(e
element)) {
remove(e);
return true;
}
}
}
return false;
}
removeFirst()
public E removeFirst() {
return remove(header
next);
}
removeLast()
public E removeLast() {
return remove(header
previous);
}
removeFirstOccurrence()
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
removeLastOccurence()
public boolean removeLastOccurrence(Object o) {
if (o==null) {
for (Entry<E> e = header
previous; e != header; e = e
previous) {
if (e
element==null) {
remove(e);
return true;
}
}
} else {
for (Entry<E> e = header
previous; e != header; e = e
previous) {
if (o
equals(e
element)) {
remove(e);
return true;
}
}
}
return false;
}
幾個remove方法最終都是調用了一個私有方法
remove(Entry<E> e)
只是其他簡單邏輯上的區別
下面分析remove(Entry<E> e)方法
private E remove(Entry<E> e) {
if (e == header)
throw new NoSuchElementException();
// 保留將被移除的節點e的內容
E result = e
element;
// 將前一節點的next引用賦值為e的下一節點
e
previous
next = e
next;
// 將e的下一節點的previous賦值為e的上一節點
e
next
previous = e
previous;
// 上面兩條語句的執行已經導致了無法在鏈表中訪問到e節點
而下面解除了e節點對前後節點的引用
e
next = e
previous = null;
// 將被移除的節點的內容設為null
e
element = null;
// 修改size大小
size
;
modCount++;
// 返回移除節點e的內容
return result;
}
clone()
public Object clone() {
LinkedList<E> clone = null;
try {
clone = (LinkedList<E>) super
clone();
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
clone
header = new Entry<E>(null
null
null);
clone
header
next = clone
header
previous = clone
header;
clone
size =
;
clone
modCount =
;
for (Entry<E> e = header
next; e != header; e = e
next)
clone
add(e
element);
return clone;
}
調用父類的clone()方法初始化對象鏈表clone
將clone構造成一個空的雙向循環鏈表
之後將header的下一個節點開始將逐個節點添加到clone中
最後返回克隆的clone對象
toArray()
public Object[] toArray() {
Object[] result = new Object[size];
int i =
;
for (Entry<E> e = header
next; e != header; e = e
next)
result[i++] = e
element;
return result;
}
創建大小和LinkedList相等的數組result
遍歷鏈表
將每個節點的元素element復制到數組中
返回數組
toArray(T[] a)
public <T> T[] toArray(T[] a) {
if (a
length < size)
a = (T[])java
lang
reflect
Array
newInstance(
a
getClass()
getComponentType()
size);
int i =
;
Object[] result = a;
for (Entry<E> e = header
next; e != header; e = e
next)
result[i++] = e
element;
if (a
length > size)
a[size] = null;
return a;
}
先判斷出入的數組a的大小是否足夠
若大小不夠則拓展
這裡用到了發射的方法
重新實例化了一個大小為size的數組
之後將數組a賦值給數組result
遍歷鏈表向result中添加的元素
最後判斷數組a的長度是否大於size
若大於則將size位置的內容設置為null
返回a
從代碼中可以看出
數組a的length小於等於size時
a中所有元素被覆蓋
被拓展來的空間存儲的內容都是null
若數組a的length的length大於size
則
至size
位置的內容被覆蓋
size位置的元素被設置為null
size之後的元素不變
為什麼不直接對數組a進行操作
要將a賦值給result數組之後對result數組進行操作?
LinkedList的Iterator
除了Entry
LinkedList還有一個內部類
ListItr
ListItr實現了ListIterator接口
可知它是一個迭代器
通過它可以遍歷修改LinkedList
在LinkedList中提供了獲取ListItr對象的方法
listIterator(int index)
public ListIterator<E> listIterator(int index) {
return new ListItr(index);
}
該方法只是簡單的返回了一個ListItr對象
LinkedList中還有通過集成獲得的listIterator()方法
該方法只是調用了listIterator(int index)並且傳入
下面詳細分析ListItr
private class ListItr implements ListIterator<E> {
// 最近一次返回的節點
也是當前持有的節點
private Entry<E> lastReturned = header;
// 對下一個元素的引用
private Entry<E> next;
// 下一個節點的index
private int nextIndex;
private int expectedModCount = modCount;
// 構造方法
接收一個index參數
返回一個ListItr對象
ListItr(int index) {
// 如果index小於
或大於size
拋出IndexOutOfBoundsException異常
if (index <
|| index > size)
throw new IndexOutOfBoundsException(
Index:
+index+
Size:
+size);
// 判斷遍歷方向
if (index < (size >>
)) {
// next賦值為第一個節點
next = header
next;
// 獲取指定位置的節點
for (nextIndex=
; nextIndex<index; nextIndex++)
next = next
next;
} else {
// else中的處理和if塊中的處理一致
只是遍歷方向不同
next = header;
for (nextIndex=size; nextIndex>index; nextIndex
)
next = next
previous;
}
}
// 根據nextIndex是否等於size判斷時候還有下一個節點(也可以理解為是否遍歷完了LinkedList)
public boolean hasNext() {
return nextIndex != size;
}
// 獲取下一個元素
public E next() {
checkForComodification();
// 如果nextIndex==size
則已經遍歷完鏈表
即沒有下一個節點了(實際上是有的
因為是循環鏈表
任何一個節點都會有上一個和下一個節點
這裡的沒有下一個節點只是說所有節點都已經遍歷完了)
if (nextIndex == size)
throw new NoSuchElementException();
// 設置最近一次返回的節點為next節點
lastReturned = next;
// 將next
向後移動一位
next = next
next;
// index計數加
nextIndex++;
// 返回lastReturned的元素
return lastReturned
element;
}
public boolean hasPrevious() {
return nextIndex !=
;
}
// 返回上一個節點
和next()方法相似
public E previous() {
if (nextIndex ==
)
throw new NoSuchElementException();
lastReturned = next = next
previous;
nextIndex
;
checkForComodification();
return lastReturned
element;
}
public int nextIndex() {
return nextIndex;
}
public int previousIndex() {
return nextIndex
;
}
// 移除當前Iterator持有的節點
public void remove() {
checkForComodification();
Entry<E> lastNext = lastReturned
next;
try {
LinkedList
this
remove(lastReturned);
} catch (NoSuchElementException e) {
throw new IllegalStateException();
}
if (next==lastReturned)
next = lastNext;
else
nextIndex
;
lastReturned = header;
expectedModCount++;
}
// 修改當前節點的內容
public void set(E e) {
if (lastReturned == header)
throw new IllegalStateException();
checkForComodification();
lastReturned
element = e;
}
// 在當前持有節點後面插入新節點
public void add(E e) {
checkForComodification();
// 將最近一次返回節點修改為header
lastReturned = header;
addBefore(e
next);
nextIndex++;
expectedModCount++;
}
// 判斷expectedModCount和modCount是否一致
以確保通過ListItr的修改操作正確的反映在LinkedList中
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
下面是一個ListItr的使用實例
LinkedList<String> list = new LinkedList<String>();
list
add(
First
);
list
add(
Second
);
list
add(
Thrid
);
System
out
println(list);
ListIterator<String> itr = list
listIterator();
while (itr
hasNext()) {
System
out
println(itr
next());
}
try {
System
out
println(itr
next());// throw Exception
} catch (Exception e) {
// TODO: handle exception
}
itr = list
listIterator();
System
out
println(list);
System
out
println(itr
next());
itr
add(
new node
);
System
out
println(list);
itr
add(
new node
);
System
out
println(list);
System
out
println(itr
next());
itr
set(
modify node
);
System
out
println(list);
itr
remove();
System
out
println(list);
結果
[First
Second
Thrid]
First
Second
Thrid
[First
Second
Thrid]
First
[First
new node
Second
Thrid]
[First
new node
new node
Second
Thrid]
Second
[First
new node
new node
modify node
Thrid]
[First
new node
new node
Thrid]
LinkedList還有一個提供Iterator的方法
descendingIterator()
該方法返回一個DescendingIterator對象
DescendingIterator是LinkedList的一個內部類
public Iterator<E> descendingIterator() {
return new DescendingIterator();
}
下面分析詳細分析DescendingIterator類
private class DescendingIterator implements Iterator {
// 獲取ListItr對象
final ListItr itr = new ListItr(size());
// hasNext其實是調用了itr的hasPrevious方法
public boolean hasNext() {
return itr
hasPrevious();
}
// next()其實是調用了itr的previous方法
public E next() {
return itr
previous();
}
public void remove() {
itr
remove();
}
}
從類名和上面的代碼可以看出這是一個反向的Iterator
代碼很簡單
都是調用的ListItr類中的方法
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26953.html