LinkedList源码阅读笔记
初始化
- 无参的
public LinkedList() {
}
- 初始化的同时添加一个Collection
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
- addAll(c)方法待会在add的时候会讲。
成员变量们
transient int size = ;
transient Node<E> first;
transient Node<E> last;
Node类
他是LinkedList的内部private类。
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;
}
}
看得出他可以向前指,也可以向后指。
增加
有 add,addAll,addFirst,addLast
add(E e)
public boolean add(E e) {
linkLast(e);
return true;
}
直接默认添加在link尾部。
- linkLast(E e)
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;//final域指向last节点
final Node<E> newNode = new Node<>(l, e, null);//构造新节点作为最后一个节点,next = null
last = newNode;//设置新节点为最后一个节点
if (l == null)//如果这是一个空链表(null==first==last)
first = newNode;
else//不是空链表,让原last的next指针指向新节点
l.next = newNode;
size++;
modCount++;//增加list结构改变次数,用于规避并发产生的错误
}
add(int index, E element)
根据索引添加,跟ArrayList差不多,index从0开始。
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
- 首先调用checkPositionIndex检查是否越界。
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= && index <= size;
}
就是很平常的跟size和0比较。但是请注意,index是可以 == size的。
- 然后如果index == size ,表示我想在最后一个元素的后面添加e。那就直接调用刚刚的linkLast就可以了。
- 如果不是就需要调用node(index)和void linkBefore(E e, Node succ) {}了。
- node(int index)
/** * Returns the (non-null) Node at the specified element index. */ Node<E> node(int index) { // assert isElementIndex(index); if (index < (size >> )) { Node<E> x = first; for (int i = ; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (int i = size - ; i > index; i--) x = x.prev; return x; } }
通过双向节点的好处和分为两段遍历减少遍历量提高性能。注意这个地方用不成二分法(二分法需要数组的下标来和查询的index比较)。
如果是前半段就从0往后遍历,直到index,返回即可。
如果是后半段就从后往前遍历,直到index,返回即可。
- linkBefore(E e, Node succ)
/** * Inserts element e before non-null Node succ. */ 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; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; }
- 需要四步:pred.next = newNode;nowNode.prev=pred;newNode.next=succ;succ.prev=newNode;
- 其中关于设置newNode的前后指针的在new Node<>(pred,e,succ);完成。
批量增加 boolean addAll(Collection
/**
* Appends all of the elements in the specified collection to the end of
* this list, in the order that they are returned by the specified
* collection's iterator. The behavior of this operation is undefined if
* the specified collection is modified while the operation is in
* progress. (Note that this will occur if the specified collection is
* this list, and it's nonempty.)
*
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
批量增加 boolean addAll(int index, Collection
/**
* Inserts all of the elements in the specified collection into this
* list, starting at the specified position. Shifts the element
* currently at that position (if any) and any subsequent elements to
* the right (increases their indices). The new elements will appear
* in the list in the order that they are returned by the
* specified collection's iterator.
*
* @param index index at which to insert the first element
* from the specified collection
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == )
return false;
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;//J2SE 提供的最后一个批注是 @SuppressWarnings。该批注的作用是给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默。
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
- 总体思路就是检查越界,被插入节点succ,被插入节点的上一个节点pred,注意要考虑到在link尾部插入的情况(当index==size就没有succ了),然后遍历c.toArray(),让一个个数据链接到pred.next(注意要考虑到在空link中插入collection),遍历完成后把succ和最新的pred链接起来。
删除
返回并删除第一个元素poll()
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
- unlinkFirst(f)
/**
* Unlinks non-null first node 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;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
从此列表所表示的堆栈处弹出一个元素pop()
/**
* Pops an element from the stack represented by this list. In other
* words, removes and returns the first element of this list.
*
* <p>This method is equivalent to {@link #removeFirst()}.
*
* @return the element at the front of this list (which is the top
* of the stack represented by this list)
* @throws NoSuchElementException if this list is empty
* @since 1.6
*/
public E pop() {
return removeFirst();
}
返回并删除第一个元素
- removeFirst():
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
remove == removeFirst
删除第一个出现的元素removeFirstOccurrence(Object o) == remove(Object o)
- 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;
}
可以看出LinkedList是支持元素为空的。
为null和非null分开unlink()
- unlink(Node node)
/**
* Unlinks non-null node x.
*/
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) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
移除最后一次出现的指定元素(从头部到尾部遍历列表时)removeLastOccurrence
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;
}
因为有prev指针的存在,所以跟removeFirstOccurrence差不多。
修改 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;
}
检查越界,通过node(int index)找到该节点,修改该节点。
查询 get
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
emmmmmmm 还是node(int index)