LinkedList
JDK中的LinkedList是繼承自AbstractSequentialList,並實現了List、Deque、Queue等接口,並支持拷貝和序列化。
public class LinkedList<E> extends AbstractSequentialList<E> implements
List<E>, Deque<E>, Queue<E>, Cloneable, Serializable
先來閱讀以下注釋,瞭解一下概況:
LinkedList是List的一個實現,基於雙向鏈表結構。所有的操作,包括添加,刪除,元素替換都是支持的。
支持所有的元素,包括null。
這個類在你需要類似隊列的行爲的時候會用到。也會在你期望你的列表包含零個或者一個元素,但是又能擴展到更大數量的元素的時候。一般來說,你在不需要使用隊列類似的功能的時候,最好使用ArrayList。
Link類是一個內部靜態類,包括前驅和後繼以及數據,實現了鏈表節點的功能。
LinkIterator是一個實現ListIterator接口的內部靜態類,實現了鏈表的遍歷功能。
ReverseLinkIterator是一個實現了Iterator接口的內部類,實現了逆序遍歷的功能。
LinkedList使用一個叫做voidLink的空節點記錄鏈表的頭指針和尾指針,voidLink.previous是尾指針,voidLink.next是頭指針。在沒有結點時,鏈表的頭尾都指向voidLink自身。
LinkedList包含以下幾個主要操作的函數add、addAll、contains、get、indexOf、remove、push、pop、set等。下面一一來看看它們的實現。
首先是add,函數實現如下:
public void add(int location, E object) {
if (location >= 0 && location <= size) {
Link<E> link = voidLink;
if (location < (size / 2)) {
for (int i = 0; i <= location; i++) {
link = link.next;
}
} else {
for (int i = size; i > location; i--) {
link = link.previous;
}
}
Link<E> previous = link.previous;
Link<E> newLink = new Link<E>(object, previous, link);
previous.next = newLink;
link.previous = newLink;
size++;
modCount++;
} else {
throw new IndexOutOfBoundsException();
}
}
add函數可以實現在指定位置添加指定的對象數據。如果位置超出範圍,會拋出IndexOutOfBoundsException異常。
如果插入的位置location在鏈表的前半部分,那麼就從前面開始遍歷到該位置;如果location在鏈表的後半部分,就從鏈表的尾部開始往前遍歷到該位置。LinkedList之後通過該對象數據創建一個節點,並將節點插入到鏈表中,並更新前驅和後繼結點指針,以及鏈表長度。
不帶插入位置參數location的add函數則直接將對象數據插入到鏈表尾部。之後更新voidLink的鏈表的頭尾指針。
addAll函數將一個Collection集合中的所有對象數據都插入到指定位置,實現過程與add函數基本一致,只不過是連續插入多個元素。
addFirst函數則是創建新結點並設置前驅後繼,然後更新頭指針,更新原來頭指針的前驅,代碼如下:
public void addFirst(E object) {
addFirstImpl(object);
}
private boolean addFirstImpl(E object) {
Link<E> oldFirst = voidLink.next;
Link<E> newLink = new Link<E>(object, voidLink, oldFirst);
voidLink.next = newLink;
oldFirst.previous = newLink;
size++;
modCount++;
return true;
}
addLast函數與addFirst函數實現類似。
contains函數用於搜索鏈表是否包含某個指定的對象。首先判斷該對象是否爲空,如果不爲空,則從前向後遍歷該鏈表進行元素對比查找,直到找到元素或者鏈表遍歷完爲止;如果元素爲空,則遍歷鏈表查找第一個爲空的結點。
get函數用於返回指定位置的結點數據。如果位置在鏈表長度允許範圍之內,則根據位置在前半部分或者後半部分的情況,從頭指針往後或從尾指針往前進行查找。如果位置超出範圍,拋出IndexOutOfBoundsException異常。
indexOf函數用於查找指定對象數據在鏈表中的位置。實現方法是從前往後遍歷鏈表進行對象查找,直到找到對象數據或者遍歷完鏈表。如果找到就返回位置,否則返回-1。
lastIndexOf函數與indexOf類似,但是不一樣的是它返回的是最後一次出現的位置。因此,遍歷方向需要變化,即變成從後往前進行鏈表的遍歷。
remove函數用於刪除指定位置的結點。先判斷位置在前半部分還是後半部分來確定遍歷方向,然後更新結點指針即可。另外該函數需要返回該位置的對象數據。如果位置超出範圍,也會拋出IndexOutOfBoundsException異常。
public E remove(int location) {
if (location >= 0 && location < size) {
Link<E> link = voidLink;
if (location < (size / 2)) {
for (int i = 0; i <= location; i++) {
link = link.next;
}
} else {
for (int i = size; i > location; i--) {
link = link.previous;
}
}
Link<E> previous = link.previous;
Link<E> next = link.next;
previous.next = next;
next.previous = previous;
size--;
modCount++;
return link.data;
}
throw new IndexOutOfBoundsException();
}
另外pop與push是實現了棧的功能,這兩個函數是通過刪除隊首元素和在隊尾添加元素實現的。
set函數用於替換指定位置的結點中的對象數據。只需遍歷到位置並替換數據即可。
public E set(int location, E object) {
if (location >= 0 && location < size) {
Link<E> link = voidLink;
if (location < (size / 2)) {
for (int i = 0; i <= location; i++) {
link = link.next;
}
} else {
for (int i = size; i > location; i--) {
link = link.previous;
}
}
E result = link.data;
link.data = object;
return result;
}
throw new IndexOutOfBoundsException();
}