结构

image.png

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

image.png

  1. 实现Iterable接口,支持for-each迭代器
  2. 实现List接口,元素有序、可重复
  3. 实现Deque(双向队列)接口,支持双向操作
  4. 实现Serializable接口,支持序列化和反序列化
  5. 实现Cloneable接口,支持克隆
  6. 没实现RandomAccess接口,不能支持快速的随机访问
  1. transient int size = 0;
  2. /**
  3. * 头节点
  4. * 规定:(first == null && last == null) ||
  5. * (first.prev == null && first.item != null)
  6. */
  7. transient Node<E> first;
  8. /**
  9. * 尾节点
  10. * 规定:(first == null && last == null) ||
  11. * (last.next == null && last.item != null)
  12. */
  13. transient Node<E> last;
  14. private static class Node<E> {
  15. E item;
  16. Node<E> next;
  17. Node<E> prev;
  18. Node(Node<E> prev, E element, Node<E> next) {
  19. this.item = element;
  20. this.next = next;
  21. this.prev = prev;
  22. }
  23. }

创建

  1. // 构造一个空列表
  2. public LinkedList() {
  3. }
  4. // 构造一个列表,该列表包含指定集合的元素,其顺序由集合的迭代器返回
  5. public LinkedList(Collection<? extends E> c) {
  6. this();
  7. addAll(c);
  8. }

add

add(e)

将指定的元素增加到列表末尾

  1. // 将指定的元素增加到列表末尾
  2. public boolean add(E e) {
  3. linkLast(e);
  4. return true;
  5. }
  6. // 将节点e作为最后一个节点链接到列表上
  7. void linkLast(E e) {
  8. final Node<E> l = last;
  9. // 创建一个以e为item属性、原先的last节点为prev的属性创建一个链表节点
  10. final Node<E> newNode = new Node<>(l, e, null);
  11. // last赋值为该新建节点
  12. last = newNode;
  13. // 原先的last节点为null,说明列表中没有元素
  14. if (l == null)
  15. // 头节点、尾节点都是这个新建节点
  16. first = newNode;
  17. else // 原先的last节点不为null,说明列表中已存在元素
  18. // 原先的尾节点指向新建节点,新建节点变成尾节点
  19. l.next = newNode;
  20. // 列表实际元素数目+1
  21. size++;
  22. // 列表修改次数+1
  23. modCount++;
  24. }

add(index,e)

指定位置插入元素,逻辑上该位置及之后的元素向后移

  1. public void add(int index, E element) {
  2. // 范围检查,校验index是否越界
  3. checkPositionIndex(index);
  4. // 插入位置为尾部
  5. if (index == size)
  6. // 将节点element作为最后一个节点链接到列表上
  7. linkLast(element);
  8. else // 插入位置不是尾部
  9. // index位置前插入element元素
  10. linkBefore(element, node(index));
  11. }
  12. // 在非空节点succ之前插入元素e
  13. void linkBefore(E e, Node<E> succ) {
  14. // pred赋值为succ节点的前置节点
  15. final Node<E> pred = succ.prev;
  16. // 新建一个节点,该节点的前置节点是succ节点的前置节点,后置节点是succ
  17. final Node<E> newNode = new Node<>(pred, e, succ);
  18. // succ的前置节点设置为新建节点
  19. succ.prev = newNode;
  20. // 原先succ前置节点为空,表示succ之前是头节点
  21. if (pred == null)
  22. // 设置新建节点为头节点
  23. first = newNode;
  24. else // 原先succ不是头节点
  25. // 原先succ的前置节点pred的后置节点设置为新建节点
  26. pred.next = newNode;
  27. // 列表实际长度+1
  28. size++;
  29. // 修改次数+1
  30. modCount++;
  31. }

get

返回列表中指定位置的元素

  1. public E get(int index) {
  2. checkElementIndex(index);
  3. return node(index).item;
  4. }
  5. // 返回指定元素索引处的(非空)节点
  6. Node<E> node(int index) {
  7. // 断言index是列表元素索引
  8. // index小于列表实际长度的一半,从头部向后查找
  9. if (index < (size >> 1)) {
  10. Node<E> x = first;
  11. for (int i = 0; i < index; i++)
  12. x = x.next;
  13. return x;
  14. } else { // index大于等于实际长度的一半,从尾部向前查找
  15. Node<E> x = last;
  16. for (int i = size - 1; i > index; i--)
  17. x = x.prev;
  18. return x;
  19. }
  20. }

set

将列表中指定位置的元素替换为指定的元素

  1. // 将列表中指定位置的元素替换为指定的元素
  2. public E set(int index, E element) {
  3. // 范围检查,判断index是否越界
  4. checkElementIndex(index);
  5. // 找到index位置的节点
  6. Node<E> x = node(index);
  7. // 获取节点的旧值
  8. E oldVal = x.item;
  9. // 给节点赋新值
  10. x.item = element;
  11. // 返回节点的旧值
  12. return oldVal;
  13. }

remove

删除列表中指定位置的元素,将所有后续元素向左移。返回从列表中删除的元素

  1. // 删除列表中指定位置的元素,将所有后续元素向左移。返回从列表中删除的元素
  2. public E remove(int index) {
  3. // 范围检查,判断index是否越界
  4. checkElementIndex(index);
  5. // 从链表中解除index位置节点的链接关系
  6. return unlink(node(index));
  7. }
  8. // 从链表里删除第一次出现的指定元素
  9. public boolean remove(Object o) {
  10. // 要删除的元素是null
  11. if (o == null) {
  12. for (Node<E> x = first; x != null; x = x.next) {
  13. // == 比较null
  14. if (x.item == null) {
  15. unlink(x);
  16. return true;
  17. }
  18. }
  19. } else { // 要删除的元素不是null
  20. for (Node<E> x = first; x != null; x = x.next) {
  21. // 使用equals方法比较是否相同
  22. if (o.equals(x.item)) {
  23. unlink(x);
  24. return true;
  25. }
  26. }
  27. }
  28. return false;
  29. }
  30. // 从链表中解除非空节点x
  31. E unlink(Node<E> x) {
  32. final E element = x.item;
  33. final Node<E> next = x.next;
  34. final Node<E> prev = x.prev;
  35. // x节点前置节点为空,表示x节点是头节点
  36. if (prev == null) {
  37. // x节点后置节点设置为头节点
  38. first = next;
  39. } else { // x节点不是头节点
  40. // x的前置节点的后置节点设置为x的后置节点
  41. prev.next = next;
  42. // x的前置节点设置为null
  43. x.prev = null;
  44. }
  45. // x的后置节点是空,表示x是尾节点
  46. if (next == null) {
  47. // x的前置节点设置为尾节点
  48. last = prev;
  49. } else { // x不是尾节点
  50. // x的后置节点的前置节点设置为x的前置节点
  51. next.prev = prev;
  52. // x的后置节点设置为null
  53. x.next = null;
  54. }
  55. // x的值设置为null
  56. x.item = null;
  57. // 列表实际长度-1
  58. size--;
  59. // 修改次数+1
  60. modCount++;
  61. // 返回x节点被删除前的值
  62. return element;
  63. }

clear

从列表中删除所有元素。这个调用返回后,列表将为空(没有元素的列表)

  1. // 从列表中删除所有元素。这个调用返回后,列表将为空(没有元素的列表)
  2. public void clear() {
  3. /**
  4. * 清除节点之间的链表不是必要的,如果丢弃的节点占据了不止一代的内存,即使有一个可到达的迭代 * 器,清除节点之间关系这个操作就可以帮助分代GC
  5. *但是为了防止其他分代中存在指向这些节点的引用,
  6. * 导致GC无法回收,有必要将他们设置为null
  7. */
  8. // 从头节点开始遍历链表
  9. for (Node<E> x = first; x != null; ) {
  10. Node<E> next = x.next;
  11. // 节点信息置为null
  12. x.item = null;
  13. // 断开节点间的引用链接
  14. x.next = null;
  15. x.prev = null;
  16. // 下一个节点
  17. x = next;
  18. }
  19. // 头节点、尾节点置为null
  20. first = last = null;
  21. // 链表长度置为0
  22. size = 0;
  23. // 修改次数+1
  24. modCount++;
  25. }

疑问: 节点间引用关系不清除,直接将头节点、尾节点设置为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 image.png 如图所示,如果对该List对象使用过ListItr(迭代器)对象,并且迭代到中间阻塞了,这时候栈中就会存在一个引用指向ListIter对象,这个对象中存在lastReturned指向链表中的一个Entry,这种情况,如果链表节点之间引用没有清除就会导致整个链表无法释放,不能被GC回收

linkFirst

  1. // 将节点e作为首节点链接到列表上
  2. private void linkFirst(E e) {
  3. final Node<E> f = first;
  4. // 创建一个前置节点为null、item为e、next指向原先头节点的新节点
  5. final Node<E> newNode = new Node<>(null, e, f);
  6. // 新节点设置成头节点
  7. first = newNode;
  8. // 原先头节点为null,表示链表为空
  9. if (f == null)
  10. // 新节点设置为尾节点
  11. last = newNode;
  12. else // 不是空链表
  13. // 原先头节点的前置节点设置为新节点
  14. f.prev = newNode;
  15. // 链表实际存储元素长度+1
  16. size++;
  17. // 修改次数+1
  18. modCount++;
  19. }