思維圖+核心+架構讓你一步到位,已拿offer附真題解析
LinkedList
它是雙向連結串列,實現了所有可選的列表操作,各種元素(除了 null)都可以作為結點
這裡的索引一般都需要從頭開始遍歷,並且它是執行緒不安全的
看完這部分程式碼之後,對用 Java來寫連結串列題肯定是非常熟悉了的
常見屬性
// 連結串列的長度 transient int size = 0; // 指向首元結點的指標 transient Node<e> first; // 指向尾結點的指標 transient Node<e> last; // 序列化 ID private static final long serialVersionUID = 876323262645176354L;
構造方法
// 構造一個空連結串列
public LinkedList(){}
// 構造一個連結串列,其中的元素來自於某一個指定的集合,元素排列順序由指定集合的迭代順序決定
public LinkedList(Collection<!--? extends E--> c) {
this();
addAll(c);
}
常見方法
E getFirst()
返回連結串列中的首元結點中儲存的元素
public E getFirst() { // 直接通過例項變數獲取首元結點 final Node<e> f = first; // 當該連結串列為空連結串列時,呼叫該方法會報錯 if(f == null) { throw new NoSuchElementException(); } // 返回首元結點內的儲存的元素資訊 return f.item; }
E getLast()
返回列表中最後一個結點儲存的元素資訊
public E getLast() {
final Node<e> l = last;
if(l == null) {
throw new NoSuchElementException();
}
return l.item;
}
E removeFirst
移除首元結點並返回其元素
public E removeFirst() { final Node<e> f = first; if(f == null) { throw new NoSuchELementException(); } // 方法定義在下面 return unlinkFirst(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; // 如果下一個結點是 null,說明原來這個連結串列就只有一個元素,現在移除後就沒有多的元素了,那麼 last結點也需要設定為 null(其實這個時候移除的既是 first結點,又是 last結點) if(next == null) { last = null; } else { // 正常情況下,因為 linkedList是雙向連結串列,所以需要設定首元結點的前指標為 null, next.prev = null; } size--; // 注意是結構化修改 modCount++; return element; }
E removeLast()
移除最後一個結點並且返回其元素
public E removeLast() {
final Node<e> l = last;
if(l == null) {
throw new NoSuchElementException();
}
return unlinkLast(l);
}
private E unlinkLast(Node<e> l) {
// assert l == last && l != null
final E element = l.element;
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;
}
void addFirst(E e)
將指定元素插入到連結串列的頭部
public void addFirst(E e) { // 方法定義在下面 linkFirst(e);}private void linkFirst(E e) { // 獲取原首元結點 final Node<e> f = first; // 建立一個新結點,其 prev為 null, element為 e, next為 f(即原首元結點) final Node<e> newNode = new Node<e>(null, e, f); // 設定新結點為首元結點 first = newNode; // 如果原首元結點為 null,說明曾經該連結串列是空的,故需要設定尾結點也是新結點 if(f == null) { last = newNode; } else { // 正常情況下,設定元首元結點的前驅節點為新結點 f.prev = newNode; } size++; modCount++;}
void addLast(E e)
將指定的元素新增到連結串列的末尾
public void addLast(E e) { linkLast(e);}private void linkLast(E e) { final Node<e> l = last; final Node<e> newNode = new Node<>(l, e, null); last = newNode; if(l == null) { first = newNode; } else { l.next = newNode; } size++; modCount++;}
boolean contains(Object o)
返回結果為對連結串列內是否包含指定元素的判斷
public boolean contains(Object o) { // 判斷元素的索引值,如果為 -1則表示不存在,!=判斷錯誤,返回 false;反之,返回 true return indexOf(o) != -1;}public int indexOf(Object o) { // 初始化索引下標 int index = 0; // 針對元素為 null進行遍歷 if(o == null) { for(Node<e> x = first; x != null; x = x.next;) { if(x.item == null) { // 找到了,返回索引值 return index; } // 沒找到,索引++,找下一個 index++; } } else { for(Node<e> x = first; x != null; x = x.next) { if(o.equals(x.item)) { return index; } index++; } } // 如果全部遍歷完了也沒找到,就返回 -1 return -1;}
int size()
返回連結串列中元素的個數
public int size() { // 返回例項中的 size欄位 return size;}
boolean add(E e)
將傳入的元素作為結點存到連結串列的尾部
public boolean add(E e) {
// 和 addLast()方法一樣,就多了一個返回值
linkLast(e);
return true;
}
boolean remove(Object o)
根據傳入元素的值,移除連結串列中第一個值與之匹配的結點
如果該連結串列中不包含該元素,則不會做任何改變
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;
}
E unlink(Node<e> x) {
// assert x != null;
final E element = x.element;
// 獲取當前結點的前驅和後繼結點
final Node<e> next = x.next;
final Node<e> prev = x.prev;
// 如果前驅節點為空,說明當前結點就是首元結點,則只需要修改下一個結點為首元結點
if(prev == null) {
first = next;
} else {
// 否則修改待刪除結點的 前驅節點的後繼 為 後繼節點的前驅
prev.next = x.next;
x.prev = null;
}
// 同理
if(next == null) {
last = prev;
} else {
next.prev = x.prev;
x.next = null;
}
x.item = null;
size--;
// 結構化修改
modCount++;
return element;
}
E remove(int index)
移除連結串列中處於指定索引位置的結點,並返回其中的元素
public E remove(int index) {
// 檢查索引的合理性
checkElementIndex(index);
// 呼叫實際從連結串列中移除某個結點的方法
return unlink(node(index));
}
boolean addAll(Collection c)
將引數集合內的所有元素,新增到連結串列的末尾
新增的順序依據該集合的迭代順序
public boolean addAll(Collection<!--? extend E--> c) { // 將待插入的位置傳過去,即呼叫下方的 return addAll(size, c);}
boolean addAll(int index, Collection c)
在指定位置之後插入集合內的元素
public boolean addAll(int index, Collection<!--? extends E--> c) {
checkPositionIndex(index);
// 將集合轉換為陣列,方便取用
Object[] a = c.toArray();
// 明確待新增的元素個數
int numNew = a.length;
if(numNew == 0) {
return false;
}
// 宣告結點,pred為待插入位置的前一個結點;succ是待插入位置的後一個結點
Node<e> pred, succ;
// 如果 index == size,則表示要在連結串列末端追加,此時尾結點即 pred,因為此時沒有下一個結點,所以 succ = null
if(index == size) {
succ = null;
pred = last;
} else {
// 依據所以獲取結點,該結點現在的位置就是將來插入的結點的位置,所以該結點會成為新節點的下一個結點
succ = node(index);
pred = succ.prev;
}
// 遍歷陣列中的元素
for(Object o : a) {
// 將元素包裹成結點物件
E e = (E) o;
// 此時新構造的結點已經指定了 前驅指標 指向的元素
Node<e> newNode = new Node<>(pred, e, null);
// 如果前驅結點為 null,表明是一個空連結串列,新插入的結點即為首元結點
if(pred == null) {
first = newNode;
} else {
// 否則,就正常插入,設定前驅結點的後繼 為新插入結點
pred.next = newNode;
}
// 指標後移,獲取下一個結點
pred = newNode;
}
// 當結點全部插入之後,最後插入的那個結點和其前驅的關係已經徹底解決,接下來解決和其後繼之間的關係
// 如果 succ == null,則表明這是在連結串列末尾追加
if(succ == null) {
// 設定 last結點為最後一個新插入結點
last = pred;
} else {
// 否則在最後一個新插入的結點和後繼之間建立聯絡
pred.next = succ;
succ.prev = pred;
}
// 更新連結串列長度
size += numNew;
// 結構化修改
modCount++;
return true;
}
// 返回在指定索引處的結點
Node<e> node(int index) {
// assert isElementIndex(index)
// 如果 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;
}
}
void clear()
清空連結串列裡的所有元素,之後該連結串列將會變成一個空連結串列
public void clear() { // clearing all of the links between nodes is unnecessary // but it helps a generational GC if the discarded nodes inhabit more than one generation // is sure to free memory even if there is a reachable Iterator // 如果結點之間存在跨代引用(一個在新生代,另一個在老年代),那麼刪去連結能幫助 GC for(Node<e> x = first; x != null; ) { Node<e> next = x.next(); x.item = null; x.prev = null; x.next = null; x = next; } first = last = null; size = 0; modCount++;}
E get(int index)
返回連結串列中位於指定索引處的結點記憶體儲的元素
public E get(int index) { // 檢驗索引是否有效 checkElementIndex(index); // 通過索引找到結點,返回結點中儲存的元素 return node(index).item;}private void checkElementIndex(int index) { // 如果索引不合理就丟擲異常 if(!isElementIndex(index)) { throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }}private boolean isElementIndex(int index) { return index >= 0 && index < size;}
E set(int index, E element)
在連結串列的指定索引處用指定的值替換其結點記憶體儲的元素
public E set(int index, E element) { // 檢查索引是否合理 checkElementIndex(index); // 通過索引獲取結點 Node<e> x = node(index); // 改變結點記憶體儲的元素內容 E oldVal = x.item; x.item = element; return oldVal;}
void add(int index, E element)
在連結串列的指定位置處插入一個元素
其他結點順次後移一位
public void add(int index, E element) { // 和 checkElementIndex()的程式碼完全一樣的,都是檢查索引的合理性 checkPositionIndex(index); // 判斷如果是插到隊尾,就使用 linkLast() if(index == size) { linkLast(element); } else { // 獲取當前 index的結點,插入到該結點前 linkBefore(element, node(index)); }}// 在指定結點前新增一個結點void linkBefore(E e, Node<e> succ) { // assert succ != null; final Node<e> pred = succ.prev; final Node<e> newNode = new Node<>(pred, e, succ); succ.prev = newNode; // 如果 pred不存在,則表明 succ曾經是 first,現在 newNode在 succ之前,newNode就是 first了 if(pred == null) { first = newNode; } else { pred.next = newNode; } size++; modCount++;}
int lastIndexOf(Object o)
返回連結串列中值同參數一致的結點最後一次出現的索引位置
public int lastIndexOf(Object o) {
int index = size;
if (o == null) {
for (Node<e> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
for (Node<e> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
}
E peek()
獲取連結串列的首元結點的元素(佇列方法),首元結點不存在時,返回 null
public E peek() {
final Node<e> f = first;
// 避免直接返回 f.item時的空指標異常
return (f == null) ? null : f.item;
}
E element()
獲取連結串列的首元結點的元素(佇列方法),首元結點不存在時,報錯
public E element() {
return getFirst();
}
E poll()
移除首元結點並返回其中儲存的元素資訊,如果連結串列為空,返回 null
public E poll() {
final Node<e> f = first;
return(f == null) ? null : unlinkFirst(f);
}
E remove()
和上面那個方法差不多,只是如果連結串列為空,就報錯
public E remove() {
return removeFirst();
}
boolean offer(E e)
在連結串列的尾部新增元素
public boolean offer(E e) {
return add(e);
}
boolean offerFirst(E e)
在鏈隊的頭部新增元素(雙端佇列的方法)
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
boolean offerLast(E e)
在連結串列的尾部新增元素(雙端佇列的方法)
public boolean offerLast(E e) {
addLast(e);
return true;
}
E peekFirst()
獲取但不移除連結串列首元結點的元素,如果連結串列為空就返回 null
public E peekFirst() {
final Node<e> f = first;
return (f == null) ? null : f.item;
}
E peekLast()
獲取但不移除連結串列尾結點的元素,如果連結串列為空就返回 null
public E peekLast() {
final Node<e> l = last;
return (l == null) ? null : l.item;
}
E pollFirst()
獲取並且移除連結串列首元結點的元素,如果連結串列為空就返回 null
public E pollFirst() {
final Node<e> f = first;
return (f == null) ? null : unlinkFirst(f);
}
E pollLast()
獲取並移除連結串列尾結點的元素,如果連結串列為空就返回 null
public E pollLast() {
final Node<e> l = last;
return (l == null) ? null : unlinkLast(l);
}
void push(E e)
將一個新的元素壓入由連結串列表示的棧中,其實就是在連結串列頭部插入,只是語義上算作是壓棧的方法
public void push(E e) {
addFirst(e);
}
E pop()
將由連結串列表示的棧中的棧頂元素彈棧並返回其元素值,其實就是移除首元結點,語義上看坐彈棧
public E pop() {
return removeFirst();
}
boolean removeFirstOccurence(Object o)
在從首元結點開始遍歷連結串列時,移除指定元素第一次出現的那個結點,如果該結點不存在,則什麼都不做
public boolean removeFirstOccurence(Object o) {
return remove(o);
}
boolean removeLastOccurence(Object o)
在從首元結點開始遍歷連結串列時,移除指定元素最後一個出現的那個結點,如果該結點不存在,則什麼都不
public boolean removeLastOccurrence(Object o) {
if (o == null) {
for (Node<e> x = last; x != null; x = x.prev) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<e> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
ListIterator<e> listIterator(int index)
返回從某一個其實索引開始,由連結串列中元素組成的 列表迭代器
該迭代器是快速失敗的,如果迭代時連結串列發生了不是由本迭代器進行的結構性修改,就會丟擲一個 併發修改異常
public ListIterator<e> listIterator(int index) {
// 檢查索引合理性
checkPositionIndex(index);
// 返回由該索引開始的列表迭代器
return new ListItr(index);
}
Iterator<e> descendingIterator()
獲取相對連結串列逆序的迭代器,迭代器中的第一個元素就是尾結點
public Iterator<e> descendingIterator() {
return new DescendingIterator();
}
Object clone()
返回連結串列的淺拷貝,其中的結點及其元素沒有拷貝,引用指向的是同一個內容
public Object clone() {
// 先獲得一個空殼子
LinkedList<e> clone = superClone();
// Put clone into "virgin" state
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0;
// Initialize clone with our elements
for(Node<e> x = first; x != null; x = x.next) {
clone.add(x.item);
}
return clone;
}
private LinkedList<e> superClone() {
try {
return (LinkedList<e>) super.clone();
} catch(CloneNotSupportedException e) {
throw new InternalError(e);
}
}
Object[] toArray()
返回一個包含列表所有元素的陣列,並且同連結串列中一致的順序排列
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
// 遍歷每一個結點,取出後將內部元素賦值給陣列元素
for(Node<e> x = first; x != null; x = x.next) {
result[i++] = x.item;
}
return result;
}
T[] 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 = 0;
// result和 a指向同一個記憶體區域
Object[] result = a;
for(Node<e> x = first; x != null; x = x.next) {
result[i++] = x.item;
}
// 如果傳入的陣列的長度很長,就在最後一個元素之後設定一個 null
if(a.length > size) {
a[size] = null;
}
return a;
}
Spliteratior<e> spliterator()
建立一個定長、有序的並行迭代器
public Spliterator<e> spliterator() {
return new LLSpliterator<e>(list, -1, 0);
}
常用內部類
private class ListItr implements ListIterator<e>
記得看到過一篇部落格,批判了 next()方法裡 lastReturned的賦值
// 列表專用的迭代器,執行程式設計師在任意方向上進行遍歷和修改
// 它的 cursor指標總是位於呼叫 previous()返回的元素和 呼叫 next()返回的元素之間
// 所以針對 size為 n的列表,cursor的取值有 n+1個
// remove()和 set()方法的物件都不是 cursor,而是上一個由 next()或 previoust()返回的物件
private class ListItr implements ListIterator<e> {
// 上一個返回的元素
private Node<e> lastReturned;
// 下一個將返回的元素
private Node<e> next;
//記錄結點的索引位置
private int nextIndex;
private int expectedModCount = modCount;
// 根據索引建立迭代器,將從該索引之後的元素開始建立
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
// 判斷是否還有下個元素
public boolean hasNext() {
return nextIndex < size;
}
// 獲取下一個元素
public E next() {
// 檢查 modCount
checkForComodification();
// 判斷有沒有下一個元素
if (!hasNext())
throw new NoSuchElementException();
// 獲取將返回的元素設定成已經返回的元素
lastReturned = next;
// 設定成下一個
next = next.next;
nextIndex++;
return lastReturned.item;
}
// 判斷是否有前一個元素
public boolean hasPrevious() {
return nextIndex > 0;
}
// 獲取前一個元素
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException();
// 獲取帶返回元素的上一個結點,但是之後,lastReturned 就等於 next了
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
public int nextIndex() {
return nextIndex;
}
public int previousIndex() {
return nextIndex - 1;
}
// 移除一個元素
public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException();
Node<e> lastNext = lastReturned.next;
unlink(lastReturned);
// 用過 previous()之後,二者就相等了,此時移除了 lastReturned的同時也移除了 next,所以需要對 next重新賦值
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
// 更新元素的值
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
}
// 在當前迭代位置之後新增元素
public void add(E e) {
checkForComodification();
lastReturned = null;
// 連結串列空了
if (next == null)
linkLast(e);
else
// 連結串列沒空,新節點在 next之前
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
// 針對迭代器中的每一個元素都進行一次同一個操作
public void forEachRemaining(Consumer<!--? super E--> action) {
Objects.requireNonNull(action);
while (modCount == expectedModCount && nextIndex < size) {
action.accept(next.item);
lastReturned = next;
next = next.next;
nextIndex++;
}
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
private static class Node<e>
// 連結串列的結點
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;
}
}
private class DescendingIterator implements Iterator<e>
// 逆序的迭代器
// 把原本的 hasPrevious()變成 hasNext(),把原本的 previous()改成 next()
private class DescendingIterator implements Iterator<e> {
private final ListItr itr = new ListItr(size());
public boolean hasNext() {
return itr.hasPrevious();
}
public E next() {
return itr.previous();
}
public void remove() {
itr.remove();
}
}
static final class LLSpliterator<e> implements Spliterator<e>
static final class LLSpliterator<e> implements Spliterator<e> {
// 批量操作的單元個數,也就是說當前物件劃分後至少應當遍歷的大小
static final int BATCH_UNIT = 1 << 10; // batch array size increment
// 最大單元的大小
static final int MAX_BATCH = 1 << 25; // max batch array size;
// 當前集合
final LinkedList<e> list; // null OK unless traversed
// 當前結點
Node<e> current; // current node; null until initialized
// 預估規模大小
int est; // size estimate; -1 until first needed
int expectedModCount; // initialized when est set
// 已遍歷的大小
int batch; // batch size for splits
// 建構函式
LLSpliterator(LinkedList<e> list, int est, int expectedModCount) {
this.list = list;
this.est = est;
this.expectedModCount = expectedModCount;
}
final int getEst() {
int s; // force initialization
final LinkedList<e> lst;
// 如果還沒有初始化,此時 est = -1
if ((s = est) < 0) {
if ((lst = list) == null)
s = est = 0;
else {
expectedModCount = lst.modCount;
// current結點直接等於 LinkedList的 first屬性,也就是說之後是從連結串列的頭部開始遍歷的
current = lst.first;
s = est = lst.size;
}
}
return s;
}
// 獲得預估大小
public long estimateSize() {
return (long) getEst();
}
// 子劃分遍歷
public Spliterator<e> trySplit() {
Node<e> p;
// 初始化 est
int s = getEst();
if (s > 1 && (p = current) != null) {
int n = batch + BATCH_UNIT;
// 如果 n超過了集合大小,就取集合最大值
if (n > s)
n = s;
// 如果 n超過了上限,就取上限
if (n > MAX_BATCH)
n = MAX_BATCH;
// 將連結串列中的元素放到陣列中
Object[] a = new Object[n];
int j = 0;
do {
a[j++] = p.item;
} while ((p = p.next) != null && j < n);
// 當前結點等於遍歷後的下一個結點
current = p;
// batch等於子遍歷的大小
batch = j;
// 剩餘估計大小需要減去已分配的值
est = s - j;
// 返回一個子物件,內部本質還是基於陣列的
return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);
}
return null;
}
// 對每一個物件進行處理
public void forEachRemaining(Consumer<!--? super E--> action) {
Node<e> p;
int n;
if (action == null)
throw new NullPointerException();
// 初始化 est
if ((n = getEst()) > 0 && (p = current) != null) {
current = null;
est = 0;
// 從頭開始遍歷
do {
E e = p.item;
p = p.next;
action.accept(e);
} while (p != null && --n > 0);
}
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
}
// 嘗試預先處理
public boolean tryAdvance(Consumer<!--? super E--> action) {
Node<e> p;
if (action == null)
throw new NullPointerException();
// 初始化 est,每消費一次,est的預估大小要減一
if (getEst() > 0 && (p = current) != null) {
--est;
E e = p.item;
// 消費完畢後,current結點就是下一個結點了,一覺醒來又是美好的一天
current = p.next;
action.accept(e);
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
// 表示該迭代器是有序、定長、子類也定長的
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
</e></e></e></e></e></e></e></e>