介绍

LinkedList将做为双端链表来实现,它本身实现了List接口和Deque接口,并且实现了所有可选的列表操作,并且允许包含所有元素(包括null)。
下面是LinkedList的类图:
image.png
虽然LinkedList有get(int index)方法,但是不推荐通过for循环调用get方法来遍历它,因为get内部实际上是从头部或尾部进行遍历,get一次的时间复杂度是O(n),而ArrayList的get的时间复杂度是O(1)的。如果你要遍历LinkedList,调用iterator函数获取迭代器进行遍历是一个最佳选择。

内部结构

先看看成员变量有哪些

  1. transient int size = 0;
  2. /**
  3. * Pointer to first node.
  4. * Invariant: (first == null && last == null) ||
  5. * (first.prev == null && first.item != null)
  6. */
  7. transient Node<E> first;
  8. /**
  9. * Pointer to last node.
  10. * Invariant: (first == null && last == null) ||
  11. * (last.next == null && last.item != null)
  12. */
  13. transient Node<E> last;

size是链表的大小,first指向链表的头结点,last指向链表的尾节点,节点用Node表示。

  1. private static class Node<E> {
  2. E item;
  3. Node<E> next;
  4. Node<E> prev;
  5. Node(Node<E> prev, E element, Node<E> next) {
  6. this.item = element;
  7. this.next = next;
  8. this.prev = prev;
  9. }
  10. }

item表示当前节点下存储的数据,next指向下一个节点,prev指向上一个节点,这样通过prev和next指针连接起来就构成了链表。

构造方法

  1. public LinkedList() {
  2. }

默认构造和ArrayList有所不同,构造方法是一个空的函数,因为ArrayList底层通过数组保存数组,可以事先指定数组的大小,但是LinkedList并不需要,LinkedList通过prev和next指针来连接每一个节点,它的大小是动态变化的,也不需要扩容。
除此之外,还有第二个构造方法,传入一个Collection集合来初始化链表。

  1. public LinkedList(Collection<? extends E> c) {
  2. this();
  3. addAll(c);
  4. }
  5. public boolean addAll(Collection<? extends E> c) {
  6. // 这里初始化size是0,从0下标位置进行添加
  7. return addAll(size, c);
  8. }
  9. // 在链表的指定位置前插入一个集合作为链表
  10. public boolean addAll(int index, Collection<? extends E> c) {
  11. // 检查index的范围
  12. checkPositionIndex(index);
  13. // 集合转为数组
  14. Object[] a = c.toArray();
  15. // 获取数组的长度
  16. int numNew = a.length;
  17. // 长度为0,直接返回,不进行添加
  18. if (numNew == 0)
  19. return false;
  20. // 定义指定插入index位置的节点succ 和succ的上一个节点pred
  21. Node<E> pred, succ;
  22. // 如果添加的位置是在末尾
  23. if (index == size) {
  24. // 在末尾进行添加,pred赋值为last节点,指定位置node为null
  25. succ = null;
  26. pred = last;
  27. } else {
  28. // 不是在末尾进行添加,先根据index查到对应的节点再赋值
  29. succ = node(index);
  30. pred = succ.prev;
  31. }
  32. // 遍历待添加的元素数组,把元素添加到链表
  33. for (Object o : a) {
  34. @SuppressWarnings("unchecked") E e = (E) o;
  35. // 构造新节点,值为e,上一个节点是pred
  36. Node<E> newNode = new Node<>(pred, e, null);
  37. // 上一个节点pred等于null,说明前面没有元素,当前节点是头节点
  38. if (pred == null)
  39. // 把newNode节点赋值给头节点first
  40. first = newNode;
  41. else
  42. // 连接pred和newNode,通过next指针连接起来
  43. pred.next = newNode;
  44. // 当前节点赋值给上一个节点pred,用于下次遍历
  45. pred = newNode;
  46. }
  47. // 在末尾进行添加的,给尾节点last进行赋值即可
  48. if (succ == null) {
  49. last = pred;
  50. } else {
  51. // 不是在末尾进行的添加,还需要连接前后
  52. pred.next = succ;
  53. succ.prev = pred;
  54. }
  55. // size大小增加
  56. size += numNew;
  57. modCount++;
  58. return true;
  59. }
  60. // 根据index索引到对应的node节点
  61. Node<E> node(int index) {
  62. // assert isElementIndex(index);
  63. // 判断下标是在前半部分还是后半部分
  64. if (index < (size >> 1)) {
  65. // 从first节点顺序遍历查找
  66. Node<E> x = first;
  67. for (int i = 0; i < index; i++)
  68. x = x.next;
  69. return x;
  70. } else {
  71. // 从last节点倒序遍历查找
  72. Node<E> x = last;
  73. for (int i = size - 1; i > index; i--)
  74. x = x.prev;
  75. return x;
  76. }
  77. }

添加节点

  1. // 在头部插入节点
  2. public void addFirst(E e) {
  3. linkFirst(e);
  4. }
  5. // 在尾部插入节点
  6. public void addLast(E e) {
  7. linkLast(e);
  8. }
  9. // 在指定位置插入节点
  10. public void add(int index, E element) {
  11. // check index范围
  12. checkPositionIndex(index);
  13. // 在尾部插入,直接调用linkLast
  14. if (index == size)
  15. linkLast(element);
  16. else
  17. // 在一个节点之前插入,先通过node(index)找到指定位置的节点
  18. linkBefore(element, node(index));
  19. }
  20. // 在指定节点succ前插入节点,值为e
  21. void linkBefore(E e, Node<E> succ) {
  22. // assert succ != null;
  23. // 获取succ的上一个节点pred
  24. final Node<E> pred = succ.prev;
  25. // 构造新的节点
  26. final Node<E> newNode = new Node<>(pred, e, succ);
  27. // succ的上一个节点指针prev指向新节点newNode
  28. succ.prev = newNode;
  29. // 如果pred为null,说明succ之前本来没有节点,那么新的节点插入进来就是头节点了
  30. if (pred == null)
  31. // 头节点赋值
  32. first = newNode;
  33. else
  34. // 把pred的next指针指向新的节点,插入完毕
  35. pred.next = newNode;
  36. // 大小+1
  37. size++;
  38. modCount++;
  39. }
  40. // 在头部插入节点
  41. private void linkFirst(E e) {
  42. // 获取当前头节点的值
  43. final Node<E> f = first;
  44. // 构造新插入的节点,他的下一个节点是原来的头节点f
  45. final Node<E> newNode = new Node<>(null, e, f);
  46. // 把新节点作为头节点
  47. first = newNode;
  48. // 原来没有头节点,说明之前链表是空的
  49. if (f == null)
  50. // 新节点是第一个节点,所以尾节点也是它
  51. last = newNode;
  52. else
  53. // 原来的头节点不是空,把之前头节点的prev指针指向新的节点
  54. f.prev = newNode;
  55. // 大小+1
  56. size++;
  57. modCount++;
  58. }
  59. // 在尾部插入节点
  60. void linkLast(E e) {
  61. // 获取尾节点
  62. final Node<E> l = last;
  63. // 构造新插入的节点,上一个节点是原来的尾节点l
  64. final Node<E> newNode = new Node<>(l, e, null);
  65. // 把新节点作为尾节点
  66. last = newNode;
  67. // 原来没有尾节点,说明之前链表为空
  68. if (l == null)
  69. // 新节点是第一个节点,头节点也是它
  70. first = newNode;
  71. else
  72. // 原来的尾节点不是空,把之前尾节点的next指针指向新的节点
  73. l.next = newNode;
  74. // 大小+1
  75. size++;
  76. modCount++;
  77. }

移除节点

  1. // 移除并返回第一个元素
  2. public E removeFirst() {
  3. final Node<E> f = first;
  4. if (f == null)
  5. throw new NoSuchElementException();
  6. return unlinkFirst(f);
  7. }
  8. // 移除并返回最后一个元素
  9. public E removeLast() {
  10. final Node<E> l = last;
  11. if (l == null)
  12. throw new NoSuchElementException();
  13. return unlinkLast(l);
  14. }
  15. // 移除指定位置元素并返回
  16. public E remove(int index) {
  17. checkElementIndex(index);
  18. return unlink(node(index));
  19. }
  20. // 移除头节点f
  21. private E unlinkFirst(Node<E> f) {
  22. // assert f == first && f != null;
  23. // 先获取节点的值,用于返回
  24. final E element = f.item;
  25. // 获取f节点的下一个节点next
  26. final Node<E> next = f.next;
  27. // 清空节点的值
  28. f.item = null;
  29. // 清除next指针
  30. f.next = null; // help GC
  31. // 头节点变为next节点
  32. first = next;
  33. // 如果next节点为null,说明移除后链表没有节点了
  34. if (next == null)
  35. // last节点也是null
  36. last = null;
  37. else
  38. // next节点是头节点,所以next的prev指针指向null
  39. next.prev = null;
  40. // 大小-1
  41. size--;
  42. modCount++;
  43. return element;
  44. }
  45. // 移除尾节点
  46. private E unlinkLast(Node<E> l) {
  47. // assert l == last && l != null;
  48. // 先获取节点的值,用于返回
  49. final E element = l.item;
  50. // 获取l节点的上一个节点prev
  51. final Node<E> prev = l.prev;
  52. // 清空节点的值
  53. l.item = null;
  54. // 清空prev指针
  55. l.prev = null; // help GC
  56. // 尾节点变为尾节点的上一个节点prev
  57. last = prev;
  58. // 上一个节点为null,说明移除之后链表就没有节点了
  59. if (prev == null)
  60. // 头节点也要赋值为null
  61. first = null;
  62. else
  63. // prev作为新的尾节点,它的next需要指向null
  64. prev.next = null;
  65. // 大小-1
  66. size--;
  67. modCount++;
  68. return element;
  69. }
  70. // 移除指定元素x
  71. E unlink(Node<E> x) {
  72. // assert x != null;
  73. // 获取指定元素x的值
  74. final E element = x.item;
  75. // 获取x的下一个节点next
  76. final Node<E> next = x.next;
  77. // 获取x的上一个节点prev
  78. final Node<E> prev = x.prev;
  79. // 上一个节点为空,说明要移除的节点x是头节点
  80. if (prev == null) {
  81. // 移除后头节点应该是x的下一个节点next
  82. first = next;
  83. } else {
  84. // 更改上一个节点的next指针,指向x的next
  85. prev.next = next;
  86. // 断开x与prev节点之间的连接
  87. x.prev = null;
  88. }
  89. // 下一个节点为空,说明要移除的节点x是尾节点
  90. if (next == null) {
  91. // 移除后尾节点应该是x的上一个节点prev
  92. last = prev;
  93. } else {
  94. // 更改下一个节点的prev指针,指向x的prev
  95. next.prev = prev;
  96. // 断开x与next节点之间的连接
  97. x.next = null;
  98. }
  99. // x前后节点的指针都已经断开,接着把值item赋值为null,可以进行GC
  100. x.item = null;
  101. // 大小-1
  102. size--;
  103. modCount++;
  104. return element;
  105. }

迭代器

LinkedList的迭代器有两种,一种是ListIterator顺序迭代,一种是DescendingIterator降序迭代。

  1. private class ListItr implements ListIterator<E> {
  2. private Node<E> lastReturned;
  3. private Node<E> next;
  4. private int nextIndex;
  5. private int expectedModCount = modCount;
  6. ListItr(int index) {
  7. // assert isPositionIndex(index);
  8. next = (index == size) ? null : node(index);
  9. nextIndex = index;
  10. }
  11. public boolean hasNext() {
  12. return nextIndex < size;
  13. }
  14. public E next() {
  15. checkForComodification();
  16. if (!hasNext())
  17. throw new NoSuchElementException();
  18. lastReturned = next;
  19. next = next.next;
  20. nextIndex++;
  21. return lastReturned.item;
  22. }
  23. public boolean hasPrevious() {
  24. return nextIndex > 0;
  25. }
  26. public E previous() {
  27. checkForComodification();
  28. if (!hasPrevious())
  29. throw new NoSuchElementException();
  30. lastReturned = next = (next == null) ? last : next.prev;
  31. nextIndex--;
  32. return lastReturned.item;
  33. }
  34. public int nextIndex() {
  35. return nextIndex;
  36. }
  37. public int previousIndex() {
  38. return nextIndex - 1;
  39. }
  40. public void remove() {
  41. checkForComodification();
  42. if (lastReturned == null)
  43. throw new IllegalStateException();
  44. Node<E> lastNext = lastReturned.next;
  45. unlink(lastReturned);
  46. if (next == lastReturned)
  47. next = lastNext;
  48. else
  49. nextIndex--;
  50. lastReturned = null;
  51. expectedModCount++;
  52. }
  53. public void set(E e) {
  54. if (lastReturned == null)
  55. throw new IllegalStateException();
  56. checkForComodification();
  57. lastReturned.item = e;
  58. }
  59. public void add(E e) {
  60. checkForComodification();
  61. lastReturned = null;
  62. if (next == null)
  63. linkLast(e);
  64. else
  65. linkBefore(e, next);
  66. nextIndex++;
  67. expectedModCount++;
  68. }
  69. public void forEachRemaining(Consumer<? super E> action) {
  70. Objects.requireNonNull(action);
  71. while (modCount == expectedModCount && nextIndex < size) {
  72. action.accept(next.item);
  73. lastReturned = next;
  74. next = next.next;
  75. nextIndex++;
  76. }
  77. checkForComodification();
  78. }
  79. final void checkForComodification() {
  80. if (modCount != expectedModCount)
  81. throw new ConcurrentModificationException();
  82. }
  83. }
  84. private class DescendingIterator implements Iterator<E> {
  85. private final ListItr itr = new ListItr(size());
  86. public boolean hasNext() {
  87. return itr.hasPrevious();
  88. }
  89. public E next() {
  90. return itr.previous();
  91. }
  92. public void remove() {
  93. itr.remove();
  94. }
  95. }