介绍
LinkedList将做为双端链表来实现,它本身实现了List接口和Deque接口,并且实现了所有可选的列表操作,并且允许包含所有元素(包括null)。
下面是LinkedList的类图:
虽然LinkedList有get(int index)方法,但是不推荐通过for循环调用get方法来遍历它,因为get内部实际上是从头部或尾部进行遍历,get一次的时间复杂度是O(n),而ArrayList的get的时间复杂度是O(1)的。如果你要遍历LinkedList,调用iterator函数获取迭代器进行遍历是一个最佳选择。
内部结构
先看看成员变量有哪些
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
size是链表的大小,first指向链表的头结点,last指向链表的尾节点,节点用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;
}
}
item表示当前节点下存储的数据,next指向下一个节点,prev指向上一个节点,这样通过prev和next指针连接起来就构成了链表。
构造方法
public LinkedList() {
}
默认构造和ArrayList有所不同,构造方法是一个空的函数,因为ArrayList底层通过数组保存数组,可以事先指定数组的大小,但是LinkedList并不需要,LinkedList通过prev和next指针来连接每一个节点,它的大小是动态变化的,也不需要扩容。
除此之外,还有第二个构造方法,传入一个Collection集合来初始化链表。
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
public boolean addAll(Collection<? extends E> c) {
// 这里初始化size是0,从0下标位置进行添加
return addAll(size, c);
}
// 在链表的指定位置前插入一个集合作为链表
public boolean addAll(int index, Collection<? extends E> c) {
// 检查index的范围
checkPositionIndex(index);
// 集合转为数组
Object[] a = c.toArray();
// 获取数组的长度
int numNew = a.length;
// 长度为0,直接返回,不进行添加
if (numNew == 0)
return false;
// 定义指定插入index位置的节点succ 和succ的上一个节点pred
Node<E> pred, succ;
// 如果添加的位置是在末尾
if (index == size) {
// 在末尾进行添加,pred赋值为last节点,指定位置node为null
succ = null;
pred = last;
} else {
// 不是在末尾进行添加,先根据index查到对应的节点再赋值
succ = node(index);
pred = succ.prev;
}
// 遍历待添加的元素数组,把元素添加到链表
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
// 构造新节点,值为e,上一个节点是pred
Node<E> newNode = new Node<>(pred, e, null);
// 上一个节点pred等于null,说明前面没有元素,当前节点是头节点
if (pred == null)
// 把newNode节点赋值给头节点first
first = newNode;
else
// 连接pred和newNode,通过next指针连接起来
pred.next = newNode;
// 当前节点赋值给上一个节点pred,用于下次遍历
pred = newNode;
}
// 在末尾进行添加的,给尾节点last进行赋值即可
if (succ == null) {
last = pred;
} else {
// 不是在末尾进行的添加,还需要连接前后
pred.next = succ;
succ.prev = pred;
}
// size大小增加
size += numNew;
modCount++;
return true;
}
// 根据index索引到对应的node节点
Node<E> node(int index) {
// assert isElementIndex(index);
// 判断下标是在前半部分还是后半部分
if (index < (size >> 1)) {
// 从first节点顺序遍历查找
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
// 从last节点倒序遍历查找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
添加节点
// 在头部插入节点
public void addFirst(E e) {
linkFirst(e);
}
// 在尾部插入节点
public void addLast(E e) {
linkLast(e);
}
// 在指定位置插入节点
public void add(int index, E element) {
// check index范围
checkPositionIndex(index);
// 在尾部插入,直接调用linkLast
if (index == size)
linkLast(element);
else
// 在一个节点之前插入,先通过node(index)找到指定位置的节点
linkBefore(element, node(index));
}
// 在指定节点succ前插入节点,值为e
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
// 获取succ的上一个节点pred
final Node<E> pred = succ.prev;
// 构造新的节点
final Node<E> newNode = new Node<>(pred, e, succ);
// succ的上一个节点指针prev指向新节点newNode
succ.prev = newNode;
// 如果pred为null,说明succ之前本来没有节点,那么新的节点插入进来就是头节点了
if (pred == null)
// 头节点赋值
first = newNode;
else
// 把pred的next指针指向新的节点,插入完毕
pred.next = newNode;
// 大小+1
size++;
modCount++;
}
// 在头部插入节点
private void linkFirst(E e) {
// 获取当前头节点的值
final Node<E> f = first;
// 构造新插入的节点,他的下一个节点是原来的头节点f
final Node<E> newNode = new Node<>(null, e, f);
// 把新节点作为头节点
first = newNode;
// 原来没有头节点,说明之前链表是空的
if (f == null)
// 新节点是第一个节点,所以尾节点也是它
last = newNode;
else
// 原来的头节点不是空,把之前头节点的prev指针指向新的节点
f.prev = newNode;
// 大小+1
size++;
modCount++;
}
// 在尾部插入节点
void linkLast(E e) {
// 获取尾节点
final Node<E> l = last;
// 构造新插入的节点,上一个节点是原来的尾节点l
final Node<E> newNode = new Node<>(l, e, null);
// 把新节点作为尾节点
last = newNode;
// 原来没有尾节点,说明之前链表为空
if (l == null)
// 新节点是第一个节点,头节点也是它
first = newNode;
else
// 原来的尾节点不是空,把之前尾节点的next指针指向新的节点
l.next = newNode;
// 大小+1
size++;
modCount++;
}
移除节点
// 移除并返回第一个元素
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
// 移除并返回最后一个元素
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
// 移除指定位置元素并返回
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
// 移除头节点f
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
// 先获取节点的值,用于返回
final E element = f.item;
// 获取f节点的下一个节点next
final Node<E> next = f.next;
// 清空节点的值
f.item = null;
// 清除next指针
f.next = null; // help GC
// 头节点变为next节点
first = next;
// 如果next节点为null,说明移除后链表没有节点了
if (next == null)
// last节点也是null
last = null;
else
// next节点是头节点,所以next的prev指针指向null
next.prev = null;
// 大小-1
size--;
modCount++;
return element;
}
// 移除尾节点
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
// 先获取节点的值,用于返回
final E element = l.item;
// 获取l节点的上一个节点prev
final Node<E> prev = l.prev;
// 清空节点的值
l.item = null;
// 清空prev指针
l.prev = null; // help GC
// 尾节点变为尾节点的上一个节点prev
last = prev;
// 上一个节点为null,说明移除之后链表就没有节点了
if (prev == null)
// 头节点也要赋值为null
first = null;
else
// prev作为新的尾节点,它的next需要指向null
prev.next = null;
// 大小-1
size--;
modCount++;
return element;
}
// 移除指定元素x
E unlink(Node<E> x) {
// assert x != null;
// 获取指定元素x的值
final E element = x.item;
// 获取x的下一个节点next
final Node<E> next = x.next;
// 获取x的上一个节点prev
final Node<E> prev = x.prev;
// 上一个节点为空,说明要移除的节点x是头节点
if (prev == null) {
// 移除后头节点应该是x的下一个节点next
first = next;
} else {
// 更改上一个节点的next指针,指向x的next
prev.next = next;
// 断开x与prev节点之间的连接
x.prev = null;
}
// 下一个节点为空,说明要移除的节点x是尾节点
if (next == null) {
// 移除后尾节点应该是x的上一个节点prev
last = prev;
} else {
// 更改下一个节点的prev指针,指向x的prev
next.prev = prev;
// 断开x与next节点之间的连接
x.next = null;
}
// x前后节点的指针都已经断开,接着把值item赋值为null,可以进行GC
x.item = null;
// 大小-1
size--;
modCount++;
return element;
}
迭代器
LinkedList的迭代器有两种,一种是ListIterator顺序迭代,一种是DescendingIterator降序迭代。
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() {
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 = (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);
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
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 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();
}
}