之前已经总结了 Set, List, Map 的结构,从本文开始就介绍具体的类,本文就介绍一下 LinkedList
LinkedList 是双向链表,如上图所示,为 LinkedList 的继承实现结构。主要说下 Deque 接口, Deque 接口主要提供了对现行集合从头部和尾部对元素进行添加,删除和访问的能力。可以看下 Deque 接口定义的方法:
由于 LinkedList 实现了以上接口,故可以灵活对 LinkedList 从头部和尾部进行元素相关的操作。
存储单元
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;
}
}
以上为 LinkedList 的节点类。 LinkedList 的每一个节点都存储了自己的前向和后继节点的指针。为了从头或者尾快速访问链表中的节点, LinkedList 存储了链表的头结点 first 和尾节点 last ,并用 size 记录链表的节点数量。
添加
初始化时,构建一个空的 LinkedList,然后添加一个元素。
add(E)
add(int ,E)
addLast(E)
addAll(Collection<? extends E>)
addAll(int index, Collection<? extends E> c)
addFirst(E)
LinkedList 提供的这 6 种添加元素的方法,除了最后一种时在链表头插入之外,其他的都是在链表的尾节点插入。添加节点时,如果指定了添加节点的位置,比如 add(int,E),则需要寻找指定位置的节点。可以看下相应的逻辑:
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
首先判断插入点是否在 size 的范围之内,然后判断插入点是否在最后一个节点之后,如果是则直接插入到最后,否则会执行 node(index) 函数,找到 index 位置的节点
Node<E> node(int index) {
// assert isElementIndex(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;
}
}
node 函数在查找 index 位置的元素时并没有直接从头查找,而是将链表从中间分为前后两段,如果 ndex 位置小于链表长度的 1/2 ,则直接从头开始向后查找;如果 index 大于链表长度的 1/2 ,则从链表尾部开始向前查找。
删除
remove()
remove(int)
remove(Object)
removeFirst()
removeLast()
removeFirstOccurrence(Object)
removeLastOccurrence(Object)
以上为 LinkedList 提供的所有的删除函数。
remove(), removeFirst() 删除头节点;
removeLast() 删除尾节点;
removeFirstOccurrence 从头结点开始遍历,删除第一次出现的目标节点;
removeLastOccurrence 从尾节点开始遍历,删除第一次出现的目标节点。
清除
清除操作是执行的是 clear() 函数。
public void clear() {
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}
clear() 函数从头结点开始遍历,将每一个节点的值 item 以及前向节点和后继节点全部置空,便于进行gc。
总结
LinkedList 是双向链表,可以从头部和尾部对元素进行访问,添加和删除,可以将其看作是栈或者队列
LinkedList 的节点只保存了插入值和前向以及后继节点
在根据位置index对元素进行访问时, LinkedList 会将链表长度除以 2,如果 index 在链表的前半部分,则从头部向后开始遍历,如果 index 在后半部分,则从尾部开始向前遍历
对链表执行 clear 操作时, LinkedList 会将每一个节点的前向和后继节点的指针置空,便于gc
--END--
识别二维码,关注我们