结构

- LinkedList内部是一个链表的实现,每个节点除了保存自身数据外,还需要维护指向前、后节点的引用
- 从存储结构上来说,相较于ArrayList的数组来说,LinkedList更耗空间
- 删除、插入速度比较快;查询(除头节点、尾节点外)需要遍历,速度较慢

- 实现Iterable接口,支持for-each迭代器
- 实现List接口,元素有序、可重复
- 实现Deque(双向队列)接口,支持双向操作
- 实现Serializable接口,支持序列化和反序列化
- 实现Cloneable接口,支持克隆
- 没实现RandomAccess接口,不能支持快速的随机访问
transient int size = 0;/*** 头节点* 规定:(first == null && last == null) ||* (first.prev == null && first.item != null)*/transient Node<E> first;/*** 尾节点* 规定:(first == null && last == null) ||* (last.next == null && last.item != null)*/transient Node<E> last;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;}}
创建
// 构造一个空列表public LinkedList() {}// 构造一个列表,该列表包含指定集合的元素,其顺序由集合的迭代器返回public LinkedList(Collection<? extends E> c) {this();addAll(c);}
add
add(e)
将指定的元素增加到列表末尾
// 将指定的元素增加到列表末尾public boolean add(E e) {linkLast(e);return true;}// 将节点e作为最后一个节点链接到列表上void linkLast(E e) {final Node<E> l = last;// 创建一个以e为item属性、原先的last节点为prev的属性创建一个链表节点final Node<E> newNode = new Node<>(l, e, null);// last赋值为该新建节点last = newNode;// 原先的last节点为null,说明列表中没有元素if (l == null)// 头节点、尾节点都是这个新建节点first = newNode;else // 原先的last节点不为null,说明列表中已存在元素// 原先的尾节点指向新建节点,新建节点变成尾节点l.next = newNode;// 列表实际元素数目+1size++;// 列表修改次数+1modCount++;}
add(index,e)
指定位置插入元素,逻辑上该位置及之后的元素向后移
public void add(int index, E element) {// 范围检查,校验index是否越界checkPositionIndex(index);// 插入位置为尾部if (index == size)// 将节点element作为最后一个节点链接到列表上linkLast(element);else // 插入位置不是尾部// index位置前插入element元素linkBefore(element, node(index));}// 在非空节点succ之前插入元素evoid linkBefore(E e, Node<E> succ) {// pred赋值为succ节点的前置节点final Node<E> pred = succ.prev;// 新建一个节点,该节点的前置节点是succ节点的前置节点,后置节点是succfinal Node<E> newNode = new Node<>(pred, e, succ);// succ的前置节点设置为新建节点succ.prev = newNode;// 原先succ前置节点为空,表示succ之前是头节点if (pred == null)// 设置新建节点为头节点first = newNode;else // 原先succ不是头节点// 原先succ的前置节点pred的后置节点设置为新建节点pred.next = newNode;// 列表实际长度+1size++;// 修改次数+1modCount++;}
get
返回列表中指定位置的元素
public E get(int index) {checkElementIndex(index);return node(index).item;}// 返回指定元素索引处的(非空)节点Node<E> node(int index) {// 断言index是列表元素索引// index小于列表实际长度的一半,从头部向后查找if (index < (size >> 1)) {Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else { // index大于等于实际长度的一半,从尾部向前查找Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;}}
set
将列表中指定位置的元素替换为指定的元素
// 将列表中指定位置的元素替换为指定的元素public E set(int index, E element) {// 范围检查,判断index是否越界checkElementIndex(index);// 找到index位置的节点Node<E> x = node(index);// 获取节点的旧值E oldVal = x.item;// 给节点赋新值x.item = element;// 返回节点的旧值return oldVal;}
remove
删除列表中指定位置的元素,将所有后续元素向左移。返回从列表中删除的元素
// 删除列表中指定位置的元素,将所有后续元素向左移。返回从列表中删除的元素public E remove(int index) {// 范围检查,判断index是否越界checkElementIndex(index);// 从链表中解除index位置节点的链接关系return unlink(node(index));}// 从链表里删除第一次出现的指定元素public boolean remove(Object o) {// 要删除的元素是nullif (o == null) {for (Node<E> x = first; x != null; x = x.next) {// == 比较nullif (x.item == null) {unlink(x);return true;}}} else { // 要删除的元素不是nullfor (Node<E> x = first; x != null; x = x.next) {// 使用equals方法比较是否相同if (o.equals(x.item)) {unlink(x);return true;}}}return false;}// 从链表中解除非空节点xE unlink(Node<E> x) {final E element = x.item;final Node<E> next = x.next;final Node<E> prev = x.prev;// x节点前置节点为空,表示x节点是头节点if (prev == null) {// x节点后置节点设置为头节点first = next;} else { // x节点不是头节点// x的前置节点的后置节点设置为x的后置节点prev.next = next;// x的前置节点设置为nullx.prev = null;}// x的后置节点是空,表示x是尾节点if (next == null) {// x的前置节点设置为尾节点last = prev;} else { // x不是尾节点// x的后置节点的前置节点设置为x的前置节点next.prev = prev;// x的后置节点设置为nullx.next = null;}// x的值设置为nullx.item = null;// 列表实际长度-1size--;// 修改次数+1modCount++;// 返回x节点被删除前的值return element;}
clear
从列表中删除所有元素。这个调用返回后,列表将为空(没有元素的列表)
// 从列表中删除所有元素。这个调用返回后,列表将为空(没有元素的列表)public void clear() {/*** 清除节点之间的链表不是必要的,如果丢弃的节点占据了不止一代的内存,即使有一个可到达的迭代 * 器,清除节点之间关系这个操作就可以帮助分代GC*但是为了防止其他分代中存在指向这些节点的引用,* 导致GC无法回收,有必要将他们设置为null*/// 从头节点开始遍历链表for (Node<E> x = first; x != null; ) {Node<E> next = x.next;// 节点信息置为nullx.item = null;// 断开节点间的引用链接x.next = null;x.prev = null;// 下一个节点x = next;}// 头节点、尾节点置为nullfirst = last = null;// 链表长度置为0size = 0;// 修改次数+1modCount++;}
疑问: 节点间引用关系不清除,直接将头节点、尾节点设置为null,这个链表也是不可达的啊,为什么还要清除节点间引用关系?
代码中的注释
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翻译: 清除节点之间的链表不是必要的,如果丢弃的节点占据了不止一代的内存,即使有一个可到达的迭代器,清除节点之间关系这个操作就可以帮助分代GC
如图所示,如果对该List对象使用过ListItr(迭代器)对象,并且迭代到中间阻塞了,这时候栈中就会存在一个引用指向ListIter对象,这个对象中存在lastReturned指向链表中的一个Entry,这种情况,如果链表节点之间引用没有清除就会导致整个链表无法释放,不能被GC回收
linkFirst
// 将节点e作为首节点链接到列表上private void linkFirst(E e) {final Node<E> f = first;// 创建一个前置节点为null、item为e、next指向原先头节点的新节点final Node<E> newNode = new Node<>(null, e, f);// 新节点设置成头节点first = newNode;// 原先头节点为null,表示链表为空if (f == null)// 新节点设置为尾节点last = newNode;else // 不是空链表// 原先头节点的前置节点设置为新节点f.prev = newNode;// 链表实际存储元素长度+1size++;// 修改次数+1modCount++;}
如图所示,如果对该List对象使用过ListItr(迭代器)对象,并且迭代到中间阻塞了,这时候栈中就会存在一个引用指向ListIter对象,这个对象中存在lastReturned指向链表中的一个Entry,这种情况,如果链表节点之间引用没有清除就会导致整个链表无法释放,不能被GC回收