Java 集合- LinkedList 源码浅析
基本使用
新建一个Java类,以最基本的用法来走一遍 ArrayList 是如何运行的
package demo;
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("demo");
String s = list.get(0);
list.remove(0);
}
}
总共分为4步
- 声明一个 LinkedList
- 添加一个元素
- 获取一个元素
- 删除一个元素
开始逐步分析源码
-
声明一个 LinkedList
进入 LinkedList 的构造函数
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable{ transient int size = 0; transient Node<E> first; transient Node<E> last; public LinkedList() { } 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; } } }
可以看到,对于 LinkedList 的构造函数,其实什么也没有做。
不过需要关注
Node<E>
这个内部类,可以看到,LinkedList 的实际上是一个双向链表,对于其中的节点 node 分别存储了指向前向节点和后向节点的指针,其大致结构如下 -
添加一个元素
跟踪代码进入 add 函数,发现 add 函数如下所示
public boolean add(E e) { linkLast(e); return true; } 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++; }
可以看到,添加一个元素为直接在链表末尾加上一个节点,并返回
true
进入
linkLast(E e)
函数,可以看到主要分为3个步骤:- 记录当前最后一个节点
- 声明一个新的节点并将当前最后一个节点赋值给新节点的
prev
- 将当前的
last
指向新节点
与 ArrayList 的 add 相同,这里也通过
modCount
记录了当前的操作次数 -
获取一个元素
public E get(int index) { checkElementIndex(index); return node(index).item; }
首先检查传入的
index
是否合法然后进入
node(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; } }
可以看到,这里查找元素使用了折半查找的方法,如果小于一半从
fisrt
开始从前往后查找,如果大于则从last
从后往前 -
删除一个元素
public E remove(int index) { checkElementIndex(index); return unlink(node(index)); } 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; }
可以看到,
remove
实际上是走到了unlink
函数中,这个操作具体可以分为4个步骤:- 记录当前节点的 prev 和 next 节点
- 如果
prev
为空则当前节点为首节点,将fisrt
指向当前节点的下一个节点即可;否则将前节点的next
指向当前节点的后一个节点 - 如果
next
为空则当前节点为尾结点,将last
指向当前节点的上一个节点即可;否则将当前节点的prev
指向当前节点的前一个节点 - 当前节点置空,
size
长度减一
最后返回删除的元素和增加操作数
整个流程可以用下图总结