Java集合源码—LinkedList

朴灿烈づ我的快乐病毒、 2021-11-10 14:36 558阅读 0赞

LinkedList源码分析

1.LinkedList概述

  • LinkedList底层是一个双链表(非循环), 是一个直线型的链表结构
  • 因为实现方式是链表, 所以在插入或删除时的效率较高, 但是因为其内存空间并不一定连续, 遍历时需要从头到尾访问一遍, 所以查找的效率并不是很高

    public class LinkedList

    1. extends AbstractSequentialList<E>
    2. implements List<E>, Deque<E>, Cloneable, java.io.Serializable

    {

    }

  • 继承AbstractSequentialList 抽象类, 在遍历LinkedList的时候, 官方更推荐使用迭代器. (虽然LinkedList也提供了get(int index)方法, 但是每次调用该方法, 都需要从链表的头部或尾部进行遍历, 复杂度是O(n) ),

  • 实现了List接口, 提供List接口中所有方法的实现
  • 实现了Cloneable接口, 支持克隆(浅克隆). 通过Object.clone()方法的到的Object对象转化为了LinkedList, 并把内部的实例域都置空, 然后把背靠背的LinkedList结点中的每个值都拷贝到clone中
  • 实现了Deque接口, 可以用作双端队列
  • 实现了Serializable接口, 支持序列化.

2.类中的属性

  1. //链表的长度
  2. transient int size = 0;
  3. //指向头结点的指针
  4. transient Node<E> first;
  5. //指向尾节点的指针
  6. transient Node<E> last;

我们来看一下这里Node类的属性 :

  1. private static class Node<E> {
  2. //值域
  3. E item;
  4. //指向后的指针
  5. Node<E> next;
  6. //向前的指针
  7. Node<E> prev;
  8. Node(Node<E> prev, E element, Node<E> next) {
  9. this.item = element;
  10. this.next = next;
  11. this.prev = prev;
  12. }
  13. }

Node类作为一个私有的静态内部类, 是每个节点的数据结构

3.类的构造器

1>无参构造器

  1. public LinkedList() {
  2. }

2>带参构造器

  1. public LinkedList(Collection<? extends E> c) {
  2. this();
  3. addAll(c);
  4. }

相比于ArrayList, LinkedList的构造方法简洁了很多

4.常用方法

1>add() , linkLast(), addLast()

  1. public boolean add(E e) {
  2. //默认将节点添加在链表末尾(尾插)
  3. linkLast(e);
  4. return true;
  5. }
  6. //分析一下linkLast(e)方法
  7. void linkLast(E e) {
  8. //获取当前的尾节点
  9. final Node<E> l = last;
  10. //new出一个赋值的新节点
  11. final Node<E> newNode = new Node<>(l, e, null);
  12. //新节点变成尾节点
  13. last = newNode;
  14. //当链表还是空时, 新加入的这个节点就是头节点了
  15. if (l == null)
  16. first = newNode;
  17. else
  18. l.next = newNode;
  19. //长度增加
  20. size++;
  21. modCount++;
  22. }
  23. //同样的, addLast()方法也是调用了linkLast()方法
  24. public void addLast(E e) {
  25. linkLast(e);
  26. }

2>addFirst() ,linkFirst()

  1. public void addFirst(E e) {
  2. //调用头插方法
  3. linkFirst(e);
  4. }
  5. //linkFirst()与linkLast 相对应, 使用头插法插入新元素
  6. private void linkFirst(E e) {
  7. //获取第一个头结点
  8. final Node<E> f = first;
  9. //new出新节点, 并让其next指向之前的头
  10. final Node<E> newNode = new Node<>(null, e, f);
  11. //新节点成为头
  12. first = newNode;
  13. //如果之前链表为空, 那么last节点也就是新new出的这个节点
  14. if (f == null)
  15. last = newNode;
  16. else
  17. f.prev = newNode;
  18. size++;
  19. modCount++;
  20. }

3> addAll()

  1. public boolean addAll(Collection<? extends E> c) {
  2. return addAll(size, c);
  3. }
  4. //index代表
  5. public boolean addAll(int index, Collection<? extends E> c) {
  6. //检查index位置是否合法
  7. checkPositionIndex(index);
  8. //将传入的集合参数转为数组
  9. Object[] a = c.toArray();
  10. int numNew = a.length;
  11. //若集合为空, 返回失败
  12. if (numNew == 0)
  13. return false;
  14. Node<E> pred, succ;
  15. //判断插入位置是否是在尾部
  16. if (index == size) {
  17. //如果是从尾部开始插入, pred节点就是链表的尾节点, succ代表插入位置后面的节点
  18. succ = null;
  19. pred = last;
  20. } else {
  21. succ = node(index);
  22. pred = succ.prev;
  23. }
  24. for (Object o : a) {
  25. //将Object对象转化成泛型类型
  26. @SuppressWarnings("unchecked") E e = (E) o;
  27. //new出新的节点, prev指向前一个节点
  28. Node<E> newNode = new Node<>(pred, e, null);
  29. //当链表为空的时候,
  30. if (pred == null)
  31. first = newNode;
  32. else
  33. pred.next = newNode;
  34. //更新pred节点, 每插入一个, 向后移动一位
  35. pred = newNode;
  36. }
  37. //如果在尾部开始插入
  38. if (succ == null) {
  39. last = pred;
  40. } else {
  41. //否则将两边连接上
  42. pred.next = succ;
  43. succ.prev = pred;
  44. }
  45. //更新链表长度
  46. size += numNew;
  47. modCount++;
  48. return true;
  49. }
  1. //删除链表中指定元素
  2. public boolean remove(Object o) {
  3. //如果为要删除 node中值为null的节点
  4. if (o == null) {
  5. //从头到尾搜寻对应节点, 只删除碰到的第一个对应的节点
  6. for (Node<E> x = first; x != null; x = x.next) {
  7. if (x.item == null) {
  8. unlink(x);
  9. return true;
  10. }
  11. }
  12. } else {
  13. for (Node<E> x = first; x != null; x = x.next) {
  14. if (o.equals(x.item)) {
  15. unlink(x);
  16. return true;
  17. }
  18. }
  19. }
  20. return false;
  21. }
  22. //删除指定node
  23. E unlink(Node<E> x) {
  24. // assert x != null;
  25. final E element = x.item;
  26. final Node<E> next = x.next;
  27. final Node<E> prev = x.prev;
  28. /**
  29. * 其实就是把要删除节点的prev.next指向next
  30. * 把next.pre指向prev
  31. */
  32. //如果是删除第一个节点
  33. if (prev == null) {
  34. first = next;
  35. } else {
  36. prev.next = next;
  37. x.prev = null;
  38. }
  39. //如果是最后一个节点
  40. if (next == null) {
  41. last = prev;
  42. } else {
  43. next.prev = prev;
  44. //将要删除的节点中的引用置空, 方便gc
  45. x.next = null;
  46. }
  47. x.item = null;
  48. size--;
  49. modCount++;
  50. return element;
  51. }

5>removeLast() , unlinkLast()

  1. public E removeLast() {
  2. final Node<E> l = last;
  3. //若为空, 则抛出异常
  4. if (l == null)
  5. throw new NoSuchElementException();
  6. return unlinkLast(l);
  7. }
  8. private E unlinkLast(Node<E> l) {
  9. // assert l == last && l != null;
  10. final E element = l.item;
  11. final Node<E> prev = l.prev;
  12. //帮助GC
  13. l.item = null;
  14. l.prev = null; // help GC
  15. //更新为尾节点
  16. last = prev;
  17. //若链表中就一个节点, 删除完就没有节点了,
  18. if (prev == null)
  19. first = null;
  20. else
  21. prev.next = null;
  22. size--;
  23. modCount++;
  24. return element;
  25. }

6>removeFirst() , unlinkFirst()

  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. private E unlinkFirst(Node<E> f) {
  9. // assert f == first && f != null;
  10. final E element = f.item;
  11. final Node<E> next = f.next;
  12. //方便虚拟机进行GC
  13. f.item = null;
  14. f.next = null; // help GC
  15. first = next;
  16. if (next == null)
  17. last = null;
  18. else
  19. next.prev = null;
  20. size--;
  21. modCount++;
  22. return element;
  23. }

7>indexOf()

  1. public int indexOf(Object o) {
  2. int index = 0;
  3. //寻找第一个出现的指定元素的位置
  4. if (o == null) {
  5. for (Node<E> x = first; x != null; x = x.next) {
  6. if (x.item == null)
  7. return index;
  8. index++;
  9. }
  10. } else {
  11. for (Node<E> x = first; x != null; x = x.next) {
  12. if (o.equals(x.item))
  13. return index;
  14. index++;
  15. }
  16. }
  17. return -1;
  18. }

8>clear()

  1. //清空操作
  2. public void clear() {
  3. //将所有节点中的引用置空, 方便虚拟机进行GC
  4. for (Node<E> x = first; x != null; ) {
  5. Node<E> next = x.next;
  6. x.item = null;
  7. x.next = null;
  8. x.prev = null;
  9. x = next;
  10. }
  11. //头尾节点置空
  12. first = last = null;
  13. //长度清零
  14. size = 0;
  15. modCount++;
  16. }

以上基本都是LinkedList中较为核心的方法, 其他的方法大多都是利用以上的方法实现的, 这里就不拿出来细看了.

发表评论

表情:
评论列表 (有 0 条评论,558人围观)

还没有评论,来说两句吧...

相关阅读