天天看點

java源碼學習-LinkedList介紹類圖屬性類方法總結

java.util.LinkedList

  • 介紹
  • 類圖
  • 屬性
    • 内部類-Node
    • 内部類-ListItr
      • 屬性
      • 方法
        • 構造方法
        • 添加新節點元素
        • 下一個節點相關方法
        • 前一個節點相關方法
        • 移除節點
        • 替換節點
    • 内部類-DescendingIterator
    • LLSpliterator類(待完善)
  • 方法
    • 構造方法
    • 方法
    • 添加新節點元素相關方法
      • List接口的添加操作
      • Deque接口的添加操作
      • 總結(待完善)
    • 擷取節點相關方法
      • 擷取指定位置節點方法
      • 擷取首節點相關方法
      • 擷取尾節點相關方法
      • 總結(待完善)
    • 移除節點相關方法
      • 删除指定對象方法
      • 删除指定位置節點方法
      • 删除頭節點相關方法
      • 删除尾節點相關方法
      • 總結(待完善)
    • 包含節點相關方法
    • 替換節點
    • 清空清單
    • 克隆
    • 轉換成數組
    • 其他方法(待完善)
  • 總結

介紹

  • Linked(鍊)List(清單):節點之間 以 鍊的形式 進行關聯,形成的清單
  • 實作了清單接口List(能對它進行隊列操作)、雙向隊列接口Deque(能将LinkedList當作雙端隊列使用)
  • 繼承了 有序清單抽象類-AbstractSequentialList

類圖

java源碼學習-LinkedList介紹類圖屬性類方法總結

屬性

// 首節點
transient Node<E> first;

// 尾節點
transient Node<E> last;

// 節點個數
transient int size = 0;
           

内部類-Node

LinkedList中,每一個節點 都是一個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;
	}
}
           

内部類-ListItr

實作了 清單疊代器接口ListIterator

屬性

// 最近一次傳回的節點
private Node<E> lastReturned;
// 下一個節點
private Node<E> next;
// 下一個節點的index
private int nextIndex;
private int expectedModCount = modCount;
           

方法

構造方法

ListItr(int index) {
	// 如果指定位置index = size,也就是說指定位置index是清單末尾, 則下一個節點next是null
	// 否則,next 就是 以index為索引的下一個節點,其中node(index)是方法
	// assert isPositionIndex(index);
	next = (index == size) ? null : node(index);
	// 下一個節點的index = 入參index
	nextIndex = index;
}
           

添加新節點元素

@Override
public void add(E e) {
	// 如果多個線程在同時操作,就抛出異常
	checkForComodification();
	// 最近一次傳回的節點為空(特别注意這塊!!!)
	lastReturned = null;
	// 如果下一個節點為空,則在清單末尾添加新節點
	if (next == null) {
		linkLast(e);
	} else {
		// 如果下一個節點不為空,則在 next節點 之前添加新節點
		linkBefore(e, next);
	}
	// 下一個節點的index自增
	nextIndex++;
	expectedModCount++;
}

final void checkForComodification() {
	if (modCount != expectedModCount) {
		throw new ConcurrentModificationException();
	}
}
           

下一個節點相關方法

// 根據判斷 下一個節點索引 是否小于 清單節點個數 得出 是否有下一個節點
@Override
public boolean hasNext() {
	return nextIndex < size;
}

@Override
public E next() {
	checkForComodification();
	// 如果沒有下一個節點,就抛出異常
	// 第1次調用,nextIndex = 0
	// 第2次調用,nextIndex = 1
	// 第3次調用,nextIndex = 2
	// 第4次調用,nextIndex = 3,不再小于size,會抛出異常
	if (!hasNext()) {
		throw new NoSuchElementException();
	}

	// 假設清單是三個節點内容是a、b、c,從a開始,根據構造方法,next = 節點a對應的節點,nextIndex = 0
	// 第1次調用next方法,根據構造方法,lastReturned = a
	// 第2次調用next方法,由于上次調用之後next變成了b,是以 lastReturned = b
	// 第3次調用next方法,由于上次調用之後next變成了c,是以 lastReturned = c
	lastReturned = next;
	// 第1次調用, next 變成 a.next,也就是b
	// 第2次調用, next 變成 b.next,也就是c
	// 第3次調用, next 變成 c.next,也就是null
	next = next.next;
	// 下一個節點索引自增1
	// 第1次調用後, nextIndex = 1
	// 第2次調用後, nextIndex = 2
	// 第3次調用後, nextIndex = 3
	nextIndex++;
	// 傳回 最近一次傳回的節點的内容,也就是item
	return lastReturned.item;
}
           

前一個節點相關方法

注意:在調用完previous之後,next 和 lastReturned 指向的是同一個節點,而在調用完next方法之後,next 和 lastReturned 指向的不是同一個節點

@Override
public boolean hasPrevious() {
	return nextIndex > 0;
}

@Override
public E previous() {
	checkForComodification();
	// 如果是在沒有調用過next方法的時候,此時nextIndex = 0,會抛出異常
	// 在第四次調用的時候,nextIndex = 0,也會抛出異常
	if (!hasPrevious()) {
		throw new NoSuchElementException();
	}

	// 假設此時已經調用過3次next方法了,nextIndex = 3、next = null、lastReturned = c
	// 第1次調用previous方法,由于next == null,是以 next = last(最後一個節點) ,也就是next = c,此時lastReturned 也=c
	// 第2次調用previous方法,由于next(=c) != null,是以 next = next.prev(也就是c.prev=b) ,此時lastReturned 也=b
	// 第3次調用previous方法,由于next(=b) != null,是以 next = next.prev(也就是b.prev=a) ,此時lastReturned 也=a
	lastReturned = next = (next == null) ? last : next.prev;
	// 第1次調用,nextIndex從3變成2
	// 第2次調用,nextIndex從2變成1
	// 第3次調用,nextIndex從1變成0
	nextIndex--;
	// 傳回 最近一次傳回的節點的内容,也就是item
	return lastReturned.item;
}
           

移除節點

移除 目前位置的 節點

@Override
public void remove() {
	checkForComodification();
	// 如果lastReturned = null,說明沒有調用過next方法,也就不知道該移除哪個節點,抛出異常
	if (lastReturned == null) {
		throw new IllegalStateException();
	}

	// 得到 目前節點lastReturned 的 下一個節點lastNext
	// 假如說,此時 lastReturned = b,則lastNext = c
	Node<E> lastNext = lastReturned.next;
	// 移除 目前節點lastReturned(b)
	unlink(lastReturned);
	// 如果 下一個節點next = 目前節點lastReturned,說明剛執行過previous方法(特别要注意這塊!!!)
	// 如果 next 也等于 b,說明剛執行過previous方法,此時nextIndex=1
	if (next == lastReturned) {
		// 那就把 next節點 變成 c
		next = lastNext;
	} else {
		// 如果 下一個節點next != 目前節點lastReturned,說明剛執行過next方法,把下一個節點的索引自減少-1
		// 如果 下一個節點next 不等于 b,說明剛執行過next方法,此時nextIndex=2,把nextIndex變成1
		nextIndex--;
	}
	// 把 目前節點 置為null,友善垃圾回收(特别要注意這塊!!!)
	lastReturned = null;
	expectedModCount++;
}
           

替換節點

@Override
public void set(E e) {
	// 如果lastReturned = null,說明沒有調用過next方法,也就不知道該替換哪個節點,抛出異常
	if (lastReturned == null) {
		throw new IllegalStateException();
	}
	checkForComodification();
	// 把 目前節點的item替換為元素e
	lastReturned.item = e;
}
           

内部類-DescendingIterator

  • 實作了 Iterator接口
  • 提供 清單的 降序疊代
  • 依賴ListItr類,使用的都是ListItr中的方法
private class DescendingIterator implements Iterator<E> {
	private final ListItr itr = new ListItr(size());
	
	@Override
	public boolean hasNext() {
		return itr.hasPrevious();
	}
	
	@Override
	public E next() {
		return itr.previous();
	}
        
	@Override
	public void remove() {
		itr.remove();
	}
}
           

LLSpliterator類(待完善)

實作了 Spliterator接口

方法

構造方法

public LinkedList() {
}

public LinkedList(Collection<? extends E> c) {
	this();
	addAll(c);
}
           

方法

添加新節點元素相關方法

List接口的添加操作

// 繼承的是AbstraceList
@Override
public boolean add(E e) {
	linkLast(e);
	return true;
}

// 繼承的是AbstraceList
@Override
public void add(int index, E element) {
	checkPositionIndex(index);
	// 如果指定位置 = 清單大小,相當于在清單末尾添加指定元素element
	if (index == size) {
		linkLast(element);
	} else {
		linkBefore(element, node(index));
	}
}

private void checkPositionIndex(int index) {
	if (!isPositionIndex(index)) {
		throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
	}
}

private boolean isPositionIndex(int index) {
	return index >= 0 && index <= size;
}

@Override
public boolean addAll(Collection<? extends E> c) {
	return addAll(size, c);
}

@Override
public boolean addAll(int index, Collection<? extends E> c) {
	checkPositionIndex(index);

	// 把參數集合轉換成數組
	Object[] a = c.toArray();
	int numNew = a.length;
	// 如果參數集合大小=0,則傳回添加失敗
	if (numNew == 0) {
		return false;
	}

	Node<E> pred, succ;
	// 如果 指定位置index = 清單大小size,則 指定位置=null,前一個節點為清單最後一個節點last
	if (index == size) {
		succ = null;
		pred = last;
	} else {
		// 如果 指定位置index != 清單大小size,則根據index擷取指定位置節點 和 其前一個節點
		succ = node(index);
		pred = succ.prev;
	}
	// 上面這裡,如果index=0,size=0,則succ=null,pred=null
	// 上面這裡,如果index=0,size!=0,則succ=是首節點,pred=null
	// 上面這裡,如果index!=0,size!=0,則succ=index位置的節點,pred=index位置的節點 的 前置節點

	// 循環數組
	for (Object o : a) {
		@SuppressWarnings("unchecked") E e = (E) o;
		// 建立節點,建立節點 的 前置節點 = pred,後驅節點 暫時 為null
		Node<E> newNode = new Node<>(pred, e, null);
		// 如果 前置節點pred = null,說明指定位置index=0,把 建立節點 當作是 清單的 首節點
		if (pred == null) {
			first = newNode;
		} else {
			// 如果 前置節點pred 不為null,則把 前置節點pred 的 後驅節點 改為 目前建立的節點
			pred.next = newNode;
		}
		// 把 建立節點 替換為 前置節點,為的是下一次循環
		pred = newNode;
	}

	// 循環結束,如果succ=null,意味着 循環之前 index=0、size=0
	// 則把最後一次循環結束之後的pred節點 當作 清單尾節點
	if (succ == null) {
		last = pred;
	} else {
		// 循環結束,如果succ!=null,意味着 循環之前 index!=0、size!=0
		// 則把最後一次循環結束之後的pred節點 的後繼節點 設定為 之前擷取到的 index位置的節點(succ)
		// 把 index位置的節點(succ) 的 前置節點 設定為 最後一次循環結束之後的pred節點
		pred.next = succ;
		succ.prev = pred;
	}

	size += numNew;
	modCount++;
	return true;
}
           

下面來看linkFirst和linkLast

private void linkFirst(E e) {
	// 首節點f
	final Node<E> f = first;
	// 建立一個新節點newNode,newNode的前置節點為null、後繼節點為 首節點f
	final Node<E> newNode = new Node<>(null, e, f);
	// 把newNode設定為首節點
	first = newNode;
	// 如果 首節點f 為空,則代表 清單也是空的,把newNode也設定為尾節點
	if (f == null) {
		last = newNode;
	} else {
		// 如果 首節點f 不為空,則把 首節點f 的 前置節點 設定為 建立節點newNode
		f.prev = newNode;
	}
	size++;
	modCount++;
}

void linkLast(E e) {
	// 尾節點l
	final Node<E> l = last;
	// 建立一個新節點newNode,newNode的前置節點為l、後繼節點為null
	final Node<E> newNode = new Node<>(l, e, null);
	// 把newNode設定為尾節點
	last = newNode;
	// 如果 尾節點l 為空,則代表 清單也是空的,把newNode也設定為首節點
	if (l == null) {
		first = newNode;
	} else {
		// 如果 尾節點l 不為空,則把 尾節點l 的 後置節點 設定為 建立節點newNode
		l.next = newNode;
	}
	size++;
	modCount++;
}
           

Deque接口的添加操作

實際上用到的都是add相關方法

public void push(E e) {
	addFirst(e);
}

// 繼承的是Deque
@Override
public void addFirst(E e) {
	linkFirst(e);
}

// 繼承的是Deque
@Override
public void addLast(E e) {
	linkLast(e);
}

// 繼承的是Deque
@Override
public boolean offer(E e) {
	return add(e);
}

// 繼承的是Deque
@Override
public boolean offerFirst(E e) {
	addFirst(e);
	return true;
}

@Override
public boolean offerLast(E e) {
	addLast(e);
	return true;
}
           

總結(待完善)

擷取節點相關方法

擷取指定位置節點方法

// 繼承的是AbstractSequentialList
@Override
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;
}
           

擷取首節點相關方法

// 1.element、getFirst
// 繼承的是Deque
@Override
public E element() {
	return getFirst();
}

// 繼承的是Deque
// 注意 如果首節點為空 則抛出異常
@Override
public E getFirst() {
	final Node<E> f = first;
	if (f == null) {
		throw new NoSuchElementException();
	}
	return f.item;
}

// 2.peek、peekFirst
// 如果首節點為空 則傳回null
// 注意,不删除首節點
public E peek() {
	final Node<E> f = first;
	return (f == null) ? null : f.item;
}

public E peekFirst() {
	final Node<E> f = first;
	return (f == null) ? null : f.item;
}

// 3.poll、pollFirst
// 移除首節點并傳回
public E poll() {
	final Node<E> f = first;
	return (f == null) ? null : unlinkFirst(f);
}

public E pollFirst() {
	final Node<E> f = first;
	return (f == null) ? null : unlinkFirst(f);
}
           

下面來看unlinkFirst方法

private E unlinkFirst(Node<E> f) {
	// assert f == first && f != null;
	// 得到首節點的元素内容
	final E element = f.item;
	// 得到首節點的下一個節點
	final Node<E> next = f.next;
	// 把上面2個都置為null
	f.item = null;
	f.next = null; // help GC
	// 把首節點的下一個節點 作為 新的首節點
	first = next;
	// 如果新的首節點為null,則尾節點也為null
	if (next == null)
		last = null;
	else
		// 如果新的首節點不為null,則新的首節點的 前置節點 置為null
		next.prev = null;
	size--;
	modCount++;
	return element;
}
           

擷取尾節點相關方法

// 繼承的是Deque
// 注意如果 尾節點為空 則抛出異常
@Override
public E getLast() {
	final Node<E> l = last;
	if (l == null) {
		throw new NoSuchElementException();
	}
	return l.item;
}

// 如果尾節點為空 則傳回null
// 注意,不删除尾節點
public E peekLast() {
	final Node<E> l = last;
	return (l == null) ? null : l.item;
}

// 移除尾節點并傳回
public E pollLast() {
	final Node<E> l = last;
	return (l == null) ? null : unlinkLast(l);
}
           

下面來看unlinkLast方法

private E unlinkLast(Node<E> l) {
	// assert l == last && l != null;
	final E element = l.item;
	final Node<E> prev = l.prev;
	// 把尾節點的元素内容 和 尾節點的前置節點 置為null
	l.item = null;
	l.prev = null; // help GC
	// 把 尾節點 的 前置節點 作為新的尾節點
	last = prev;
	if (prev == null)
		first = null;
	else
		// 把新的尾節點的 後驅節點置為null
		prev.next = null;
	size--;
	modCount++;
	return element;
}
           

總結(待完善)

移除節點相關方法

删除指定對象方法

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.item;
	final Node<E> next = x.next;
	final Node<E> prev = x.prev;
	
	if (prev == null) {
		// 如果要删除的對象 的 前置節點為Null,說明要删除的對象是首節點,把要删除的對象 的 後置節點 作為 新的首節點
		first = next;
	} else {
		// 如果要删除的對象 的 前置節點不為Null
		// 把 要删除的對象 的 (前置節點的後置節點) 設定為 (要删除的對象 的 後置節點)
		prev.next = next;
		x.prev = null;
	}

	if (next == null) {
		// 如果要删除的對象 的 後置節點為Null,說明要删除的對象是尾節點,把要删除的對象 的 前置節點 作為 新的尾節點
		last = prev;
	} else {
		// 如果要删除的對象 的 後置節點不為Null
		// 把 要删除的對象 的 (後置節點的前置節點) 設定為 (要删除的對象 的 前置節點)
		next.prev = prev;
		x.next = null;
	}

	x.item = null;
	size--;
	modCount++;
	return element;
}
           

删除指定位置節點方法

public E remove(int index) {
	checkElementIndex(index);
	return unlink(node(index));
}
           

删除頭節點相關方法

public E pop() {
	return removeFirst();
}

public E remove() {
	return removeFirst();
}

// 删除頭結點,如果頭節點為空,則抛出異常
public E removeFirst() {
	final Node<E> f = first;
	if (f == null)
		throw new NoSuchElementException();
	return unlinkFirst(f);
}

// removeFirstOccurrence
           

另外 poll、pollFirst 也可以用于删除頭節點

删除尾節點相關方法

// 删除尾結點,如果尾節點為空,則抛出異常
public E removeLast() {
	final Node<E> l = last;
	if (l == null)
		throw new NoSuchElementException();
	return unlinkLast(l);
}

// removeLastOccurrence
           

另外 pollLast 也可以用于删除頭節點

總結(待完善)

包含節點相關方法

// 繼承的是AbstractCollection
@Override
public boolean contains(Object o) {
	return indexOf(o) != -1;
}

// 繼承的是AbstractList
@Override
public int indexOf(Object o) {
	// 從0開始循環
	int index = 0;
	if (o == null) {
		for (Node<E> x = first; x != null; x = x.next) {
			if (x.item == null) {
				// 找到第一個内容為null的節點之後就傳回
				return index;
			}
			index++;
		}
	} else {
		for (Node<E> x = first; x != null; x = x.next) {
			if (o.equals(x.item)) {
				// 找到第一個内容=指定對象o的節點之後就傳回
				return index;
			}
			index++;
		}
	}
	return -1;
}

@Override
public int lastIndexOf(Object o) {
	// 從清單末尾開始往前循環
	int index = size;
	if (o == null) {
		for (Node<E> x = last; x != null; x = x.prev) {
			index--;
			// 找到最後一個内容為null的節點之後就傳回
			if (x.item == null){
				return index;
			}
		}
	} else {
		for (Node<E> x = last; x != null; x = x.prev) {
			index--;
			// 找到最後一個内容=指定對象o的節點之後就傳回
			if (o.equals(x.item)) {
				return index;
			}
		}
	}
	return -1;
}
           

替換節點

public E set(int index, E element) {
	checkElementIndex(index);
	Node<E> x = node(index);
	E oldVal = x.item;
	x.item = element;
	return oldVal;
}
           

清空清單

// 繼承的是AbstractList
@Override
public void clear() {
	// Clearing all of the links between nodes is "unnecessary", but:
	// - 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
	for (Node<E> x = first; x != null; ) {
		// 清單的首節點開始循環,隻要節點不為空,就一直循環
		// 擷取 循環到的節點x 的 下一個節點next
		Node<E> next = x.next;
		// 把節點x的内容、前置、後置都設定為null
		x.item = null;
		x.next = null;
		x.prev = null;
		// 用 下一個節點next 作為 目前節點x,繼續循環
		x = next;
	}
	// 把首尾節點都置為null
	first = last = null;
	// 把清單大小設定尾0
	size = 0;
	modCount++;
}
           

克隆

基本比較好閱讀

@Override
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);
	}
}
           

轉換成數組

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;
}

public <T> T[] toArray(T[] a) {
	if (a.length < size)
		a = (T[])java.lang.reflect.Array.newInstance(
                                a.getClass().getComponentType(), size);
	int i = 0;
	Object[] result = a;
	for (Node<E> x = first; x != null; x = x.next)
		result[i++] = x.item;

	if (a.length > size)
		a[size] = null;

	return a;
}
           

其他方法(待完善)

總結

  • 為什麼first和last用transient修飾?