LinkedList源码探讨(基于JDK1.8)

来源:互联网 发布:mac系统格式化u盘 编辑:程序博客网 时间:2024/05/10 16:14
LinkedList类结构

通过LinkedList实现的接口可知,其支持队列操作,双向列表操作,能被克隆,支持序列化。
  1. public class LinkedList<E>
  2. extends AbstractSequentialList<E>
  3. implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
LinkedList 实现 List 接口,能对它进行队列操作。
LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
LinkedList 是非同步的。

LinkedList有三个重要的成员变量
transient int size = 0; //添加到链表中元素个数
transient Node<E> first; //头结点
transient Node<E> last; //尾结点
  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. }

LinkedList添加元素方法
  1. //头插入,在列表首部插入节点值e
  2. public void addFirst(E e) {
  3. linkFirst(e);
  4. }
  5. //头插入,即将节点值为e的节点设置为链表首节点
  6. private void linkFirst(E e) {
  7. final Node<E> f = first;
  8. //构建一个prev值为null,节点值为e,next值为f的新节点newNode
  9. final Node<E> newNode = new Node<>(null, e, f);
  10. //将newNode作为首节点
  11. first = newNode;
  12. //如果原首节点为null,即原链表为null,则链表尾节点也设置为newNode
  13. if (f == null)
  14. last = newNode;
  15. else //否则,原首节点的prev设置为newNode
  16. f.prev = newNode;
  17. size++;
  18. modCount++;
  19. }
  20. //尾插入,在列表尾部插入节点值e,该方法等价于add()
  21. public void addLast(E e) {
  22. linkLast(e);
  23. }
  24. //尾插入,在列表尾部插入节点值e
  25. public boolean add(E e) {
  26. linkLast(e);
  27. return true;
  28. }
  29. //尾插入,即将节点值为e的节点设置为链表的尾节点
  30. void linkLast(E e) {
  31. final Node<E> l = last;
  32. //构建一个prev值为l,节点值为e,next值为null的新节点newNode
  33. final Node<E> newNode = new Node<>(l, e, null);
  34. //将newNode作为尾节点
  35. last = newNode;
  36. //如果原尾节点为null,即原链表为null,则链表首节点也设置为newNode
  37. if (l == null)
  38. first = newNode;
  39. else //否则,原尾节点的next设置为newNode
  40. l.next = newNode;
  41. size++;
  42. modCount++;
  43. }
  44. //中间插入,在非空节点succ之前插入节点值e
  45. void linkBefore(E e, Node<E> succ) {
  46. // assert succ != null;
  47. final Node<E> pred = succ.prev;
  48. //构建一个prev值为succ.prev,节点值为e,next值为succ的新节点newNode
  49. final Node<E> newNode = new Node<>(pred, e, succ);
  50. //设置newNode为succ的前节点
  51. succ.prev = newNode;
  52. //如果succ.prev为null,即如果succ为首节点,则将newNode设置为首节点
  53. if (pred == null)
  54. first = newNode;
  55. else //如果succ不是首节点
  56. pred.next = newNode;
  57. size++;
  58. modCount++;
  59. }
  60. /**
  61. * 按照指定collection的迭代器所返回的元素顺序,将该collection中的所有元素添加到此链表的尾部
  62. * 如果指定的集合添加到链表的尾部的过程中,集合被修改,则该插入过程的后果是不确定的。
  63. * 一般这种情况发生在指定的集合为该链表的一部分,且其非空。
  64. * @throws NullPointerException 指定集合为null
  65. */
  66. public boolean addAll(Collection<? extends E> c) {
  67. return addAll(size, c);
  68. }
  69. //从指定的位置开始,将指定collection中的所有元素插入到此链表中,新元素的顺序为指定collection的迭代器所返回的元素顺序
  70. public boolean addAll(int index, Collection<? extends E> c) {
  71. checkPositionIndex(index); //index >= 0 && index <= size
  72. Object[] a = c.toArray();
  73. int numNew = a.length;
  74. if (numNew == 0)
  75. return false;
  76. Node<E> pred, succ; //succ指向当前需要插入节点的位置,pred指向其前一个节点
  77. if (index == size) { //说明在列表尾部插入集合元素
  78. succ = null;
  79. pred = last;
  80. } else {
  81. succ = node(index); //得到索引index所对应的节点
  82. pred = succ.prev;
  83. }
  84. //指定collection中的所有元素依次插入到此链表中指定位置的过程
  85. for (Object o : a) {
  86. @SuppressWarnings("unchecked") E e = (E) o;
  87. //将元素值e,前继节点pred“封装”为一个新节点newNode
  88. Node<E> newNode = new Node<>(pred, e, null);
  89. if (pred == null) //如果原链表为null,则新插入的节点作为链表首节点
  90. first = newNode;
  91. else
  92. pred.next = newNode;
  93. pred = newNode; //pred指针向后移动,指向下一个需插入节点位置的前一个节点
  94. }
  95. //集合元素插入完成后,与原链表index位置后面的子链表链接起来
  96. if (succ == null) { //说明之前是在列表尾部插入的集合元素
  97. last = pred; //pred指向的是最后插入的那个节点
  98. } else {
  99. pred.next = succ;
  100. succ.prev = pred;
  101. }
  102. size += numNew;
  103. modCount++;
  104. return true;
  105. }
  106. //将指定的元素(E element)插入到列表的指定位置(index)
  107. public void add(int index, E element) {
  108. checkPositionIndex(index); //index >= 0 && index <= size
  109. if (index == size)
  110. linkLast(element); //尾插入
  111. else
  112. linkBefore(element, node(index)); //中间插入
  113. }

在public void add(int index, E element)中使用到Node<E> node(int index)这个方法。此方法是返回某个index对应的结点,在遍历链表之前,先通过
if (index < (size >> 1))来判断index是否在链表的前半部分,如果在前半部分,则通过first向后遍历,也就是从前往后遍历,否则从last结点往前遍历,此种获取结点的方式避免了每次都从头遍历的弊端,也就是加快了获取速度。
  1. Node<E> node(int index) {
  2. // assert isElementIndex(index);
  3. if (index < (size >> 1)) {
  4. Node<E> x = first;
  5. for (int i = 0; i < index; i++)
  6. x = x.next;
  7. return x;
  8. } else {
  9. Node<E> x = last;
  10. for (int i = size - 1; i > index; i--)
  11. x = x.prev;
  12. return x;
  13. }
  14. }


LinkedList删除元素方法
  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. //删除非空的首节点f
  9. private E unlinkFirst(Node<E> f) {
  10. // assert f == first && f != null;
  11. final E element = f.item;
  12. final Node<E> next = f.next;
  13. f.item = null;
  14. f.next = null; // help GC
  15. first = next; //将原首节点的next节点设置为首节点
  16. if (next == null) //如果原链表只有一个节点,即原首节点,删除后,链表为null
  17. last = null;
  18. else
  19. next.prev = null;
  20. size--;
  21. modCount++;
  22. return element;
  23. }
  24. //移除尾节点,并返回该节点的元素值
  25. public E removeLast() {
  26. final Node<E> l = last;
  27. if (l == null)
  28. throw new NoSuchElementException();
  29. return unlinkLast(l);
  30. }
  31. //删除非空的尾节点l
  32. private E unlinkLast(Node<E> l) {
  33. // assert l == last && l != null;
  34. final E element = l.item;
  35. final Node<E> prev = l.prev;
  36. l.item = null;
  37. l.prev = null; // help GC
  38. last = prev; //将原尾节点的prev节点设置为尾节点
  39. if (prev == null) //如果原链表只有一个节点,则删除后,链表为null
  40. first = null;
  41. else
  42. prev.next = null;
  43. size--;
  44. modCount++;
  45. return element;
  46. }
  47. //移除此列表中指定位置上的元素
  48. public E remove(int index) {
  49. checkElementIndex(index); //index >= 0 && index < size
  50. return unlink(node(index));
  51. }
  52. //删除非空节点x
  53. E unlink(Node<E> x) {
  54. // assert x != null;
  55. final E element = x.item;
  56. final Node<E> next = x.next;
  57. final Node<E> prev = x.prev;
  58. if (prev == null) { //如果被删除节点为头节点
  59. first = next;
  60. } else {
  61. prev.next = next;
  62. x.prev = null;
  63. }
  64. if (next == null) { //如果被删除节点为尾节点
  65. last = prev;
  66. } else {
  67. next.prev = prev;
  68. x.next = null;
  69. }
  70. x.item = null; // help GC
  71. size--;
  72. modCount++;
  73. return element;
  74. }
  75. //移除列表中首次出现的指定元素(如果存在),LinkedList中允许存放重复的元素
  76. public boolean remove(Object o) {
  77. //由于LinkedList中允许存放null,因此下面通过两种情况来分别处理
  78. if (o == null) {
  79. for (Node<E> x = first; x != null; x = x.next) { //顺序访问
  80. if (x.item == null) {
  81. unlink(x);
  82. return true;
  83. }
  84. }
  85. } else {
  86. for (Node<E> x = first; x != null; x = x.next) {
  87. if (o.equals(x.item)) {
  88. unlink(x);
  89. return true;
  90. }
  91. }
  92. }
  93. return false;
  94. }
  95. //清除列表中所有节点
  96. public void clear() {
  97. // Clearing all of the links between nodes is "unnecessary", but:
  98. // - helps a generational GC if the discarded nodes inhabit
  99. // more than one generation
  100. // - is sure to free memory even if there is a reachable Iterator
  101. for (Node<E> x = first; x != null; ) {
  102. Node<E> next = x.next;
  103. x.item = null;
  104. x.next = null;
  105. x.prev = null;
  106. x = next;
  107. }
  108. first = last = null;
  109. size = 0;
  110. modCount++;
  111. }


LinkedList查找
  1. //返回列表首节点元素值
  2. public E getFirst() {
  3. final Node<E> f = first;
  4. if (f == null) //如果首节点为null
  5. throw new NoSuchElementException();
  6. return f.item;
  7. }
  8. //返回列表尾节点元素值
  9. public E getLast() {
  10. final Node<E> l = last;
  11. if (l == null) //如果尾节点为null
  12. throw new NoSuchElementException();
  13. return l.item;
  14. }
  15. //判断列表中是否包含有元素值o,返回true当列表中至少存在一个元素值e,使得(o==null?e==null:o.equals(e))
  16. public boolean contains(Object o) {
  17. return indexOf(o) != -1;
  18. }
  19. //返回指定索引处的元素值
  20. public E get(int index) {
  21. checkElementIndex(index); //index >= 0 && index < size
  22. return node(index).item; //node(index)返回指定索引位置index处的节点
  23. }
  24. //返回指定索引位置的节点
  25. Node<E> node(int index) {
  26. // assert isElementIndex(index);
  27. //折半思想,当index < size/2时,从列表首节点向后查找
  28. if (index < (size >> 1)) {
  29. Node<E> x = first;
  30. for (int i = 0; i < index; i++)
  31. x = x.next;
  32. return x;
  33. } else { //当index >= size/2时,从列表尾节点向前查找
  34. Node<E> x = last;
  35. for (int i = size - 1; i > index; i--)
  36. x = x.prev;
  37. return x;
  38. }
  39. }
  40. //正向查找,返回LinkedList中元素值Object o第一次出现的位置,如果元素不存在,则返回-1
  41. public int indexOf(Object o) {
  42. int index = 0;
  43. //由于LinkedList中允许存放null,因此下面通过两种情况来分别处理
  44. if (o == null) {
  45. for (Node<E> x = first; x != null; x = x.next) { //顺序向后
  46. if (x.item == null)
  47. return index;
  48. index++;
  49. }
  50. } else {
  51. for (Node<E> x = first; x != null; x = x.next) {
  52. if (o.equals(x.item))
  53. return index;
  54. index++;
  55. }
  56. }
  57. return -1;
  58. }
  59. //逆向查找,返回LinkedList中元素值Object o最后一次出现的位置,如果元素不存在,则返回-1
  60. public int lastIndexOf(Object o) {
  61. int index = size;
  62. //由于LinkedList中允许存放null,因此下面通过两种情况来分别处理
  63. if (o == null) {
  64. for (Node<E> x = last; x != null; x = x.prev) { //逆向向前
  65. index--;
  66. if (x.item == null)
  67. return index;
  68. }
  69. } else {
  70. for (Node<E> x = last; x != null; x = x.prev) {
  71. index--;
  72. if (o.equals(x.item))
  73. return index;
  74. }
  75. }
  76. return -1;
  77. }


LinkedList的Queue操作
  1. //获取但不移除此队列的头;如果此队列为空,则返回 null
  2. public E peek() {
  3. final Node<E> f = first;
  4. return (f == null) ? null : f.item;
  5. }
  6. //获取但不移除此队列的头;如果此队列为空,则抛出NoSuchElementException异常
  7. public E element() {
  8. return getFirst();
  9. }
  10. //获取并移除此队列的头,如果此队列为空,则返回 null
  11. public E poll() {
  12. final Node<E> f = first;
  13. return (f == null) ? null : unlinkFirst(f);
  14. }
  15. //获取并移除此队列的头,如果此队列为空,则抛出NoSuchElementException异常
  16. public E remove() {
  17. return removeFirst();
  18. }
  19. //将指定的元素值(E e)插入此列表末尾
  20. public boolean offer(E e) {
  21. return add(e);
  22. }


LinkedList的双端队列操作
  1. //获取但不移除此队列的头;如果此队列为空,则返回 null
  2. public E peek() {
  3. final Node<E> f = first;
  4. return (f == null) ? null : f.item;
  5. }
  6. //获取但不移除此队列的头;如果此队列为空,则抛出NoSuchElementException异常
  7. public E element() {
  8. return getFirst();
  9. }
  10. //获取并移除此队列的头,如果此队列为空,则返回 null
  11. public E poll() {
  12. final Node<E> f = first;
  13. return (f == null) ? null : unlinkFirst(f);
  14. }
  15. //获取并移除此队列的头,如果此队列为空,则抛出NoSuchElementException异常
  16. public E remove() {
  17. return removeFirst();
  18. }
  19. //将指定的元素值(E e)插入此列表末尾
  20. public boolean offer(E e) {
  21. return add(e);
  22. }
  23. // Deque operations
  24. //将指定的元素插入此双端队列的开头
  25. public boolean offerFirst(E e) {
  26. addFirst(e);
  27. return true;
  28. }
  29. //将指定的元素插入此双端队列的末尾
  30. public boolean offerLast(E e) {
  31. addLast(e);
  32. return true;
  33. }
  34. //获取,但不移除此双端队列的第一个元素;如果此双端队列为空,则返回 null
  35. public E peekFirst() {
  36. final Node<E> f = first;
  37. return (f == null) ? null : f.item;
  38. }
  39. //获取,但不移除此双端队列的最后一个元素;如果此双端队列为空,则返回 null
  40. public E peekLast() {
  41. final Node<E> l = last;
  42. return (l == null) ? null : l.item;
  43. }
  44. //获取并移除此双端队列的第一个元素;如果此双端队列为空,则返回 null
  45. public E pollFirst() {
  46. final Node<E> f = first;
  47. return (f == null) ? null : unlinkFirst(f);
  48. }
  49. //获取并移除此双端队列的最后一个元素;如果此双端队列为空,则返回 null
  50. public E pollLast() {
  51. final Node<E> l = last;
  52. return (l == null) ? null : unlinkLast(l);
  53. }
  54. //将一个元素推入此双端队列所表示的堆栈(换句话说,此双端队列的头部)
  55. public void push(E e) {
  56. addFirst(e);
  57. }
  58. //从此双端队列所表示的堆栈中弹出一个元素(换句话说,移除并返回此双端队列的头部)
  59. public E pop() {
  60. return removeFirst();
  61. }
  62. //从此双端队列移除第一次出现的指定元素,如果列表中不包含次元素,则没有任何改变
  63. public boolean removeFirstOccurrence(Object o) {
  64. return remove(o);
  65. }
  66. //从此双端队列移除最后一次出现的指定元素,如果列表中不包含次元素,则没有任何改变
  67. public boolean removeLastOccurrence(Object o) {
  68. //由于LinkedList中允许存放null,因此下面通过两种情况来分别处理
  69. if (o == null) {
  70. for (Node<E> x = last; x != null; x = x.prev) { //逆向向前
  71. if (x.item == null) {
  72. unlink(x);
  73. return true;
  74. }
  75. }
  76. } else {
  77. for (Node<E> x = last; x != null; x = x.prev) {
  78. if (o.equals(x.item)) {
  79. unlink(x);
  80. return true;
  81. }
  82. }
  83. }
  84. return false;
  85. }
事实上,LinkedList的队列操作,最终都是转化为链表的操作。


LinkedList的Fail-Fast机制

LinkedList也采用了快速失败的机制,通过记录modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。

 
对addAll函数的思考
  1. public boolean addAll(int index, Collection<? extends E> c) {
  2. checkPositionIndex(index);
  3. Object[] a = c.toArray();
  4. int numNew = a.length;
  5. if (numNew == 0)
  6. return false;
  7. Node<E> pred, succ;
  8. if (index == size) {
  9. succ = null;
  10. pred = last;
  11. } else {
  12. succ = node(index);
  13. pred = succ.prev;
  14. }
  15. for (Object o : a) {
  16. @SuppressWarnings("unchecked") E e = (E) o;
  17. Node<E> newNode = new Node<>(pred, e, null);
  18. if (pred == null)
  19. first = newNode;
  20. else
  21. pred.next = newNode;
  22. pred = newNode;
  23. }
  24. if (succ == null) {
  25. last = pred;
  26. } else {
  27. pred.next = succ;
  28. succ.prev = pred;
  29. }
  30. size += numNew;
  31. modCount++;
  32. return true;
  33. }

在addAll函数中,传入一个集合参数和插入位置,然后将集合转化为数组,然后再遍历数组,挨个添加数组的元素,但是问题来了,为什么要先转化为数组再进行遍历,而不是直接遍历集合呢?从效果上两者是完全等价的,都可以达到遍历的效果。关于为什么要转化为数组的问题,我的思考如下:1. 如果直接遍历集合的话,那么在遍历过程中需要插入元素,在堆上分配内存空间,修改指针域,这个过程中就会一直占用着这个集合,考虑正确同步的话,其他线程只能一直等待。2. 如果转化为数组,只需要遍历集合,而遍历集合过程中不需要额外的操作,所以占用的时间相对是较短的,这样就利于其他线程尽快的使用这个集合。说白了,就是有利于提高多线程访问该集合的效率,尽可能短时间的阻塞。





原创粉丝点击