LinkedList 源码解析

187 阅读3分钟

LinkedList 源码解析

LinkedList 简介

LinkedList是Java集合中的其中一种容器,底层是用双向链表来实现的,继承了AbstractSequentialList抽象类,实现了List, Deque, Cloneable, Serializable接口

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{

LinkedList 构造函数

执行下列代码

List<String> list = new LinkedList();

解析LinkedList 构造函数

  /**
   * Constructs an empty list.
   */
  public LinkedList() {
  }

查看LinkedList的默认构造方法,什么都没有,只是基本的在堆内存中分配了空间;在继续查看其初始属性情况

  /**
   * 存储数据长度
   */
  transient int size = 0;

  /**
   * 第一个元素
   */
  transient Node<E> first;

  /**
   * 最后一个元素
   */
  transient Node<E> last;

LinkedList 默认初始化的堆内存情况,如下图所示(图片来自[zhuanlan.zhihu.com/p/28101975]

其三个属性的初始情况是first,last都为null,而size是基础类型,初始化为0;

LinkedList add()方法

执行下列代码

      List<String> list = new LinkedList();
      list.add("张三");
      list.add("李四");
      list.add("王五");
      list.add("赵六");

解析add()方法,查看源码

public boolean add(E e) {
      linkLast(e);
      return true;
  }
  
  /**
   * Links e as last element.
   */
  void linkLast(E e) {
      final Node<E> l = last; // 第一次添加,last = null,所以l = null
      final Node<E> newNode = new Node<>(l, e, null); //初始化一个新的Node对象
      last = newNode; // 把新的Node对象赋值给last
      if (l == null) 
          first = newNode; // 把新Node对象赋值给first;这里last = first = newNode(意味着第一个和最后一个元素都指向同一个Node对象)
      else
          l.next = newNode;
      size++; //数据长度+1
      modCount++; // 修改次数+1
  }
  
  private static class Node<E> {
      E item; //具体数据(李四)
      Node<E> next; // 下一个元素引用地址(null)
      Node<E> prev; // 上一个元素引用地址(null)

      Node(Node<E> prev, E element, Node<E> next) {
          this.item = element;
          this.next = next;
          this.prev = prev;
      }
  }

LinkedList 添加第一个元素的堆内存情况,如下图所示(图片来自[zhuanlan.zhihu.com/p/28101975]

从图中可知LinkedList添加第一个元素的jvm内存分布,继续添加第二个元素李四,在重新走一遍add方法

    /**
     * Links e as last element.
     */
    void linkLast(E e) {
        final Node<E> l = last; // 第二次添加,last = 第一个元素(李四),所以l = 第一个元素(李四)
        final Node<E> newNode = new Node<>(l, e, null); //初始化一个新的Node对象
        last = newNode; // 把新的Node对象赋值给last
        if (l == null) // 此时l不为空,为第一个元素(李四)
            first = newNode; 
        else
            l.next = newNode; // 第一个元素(李四)的下一个元素赋值为新的Node对象
        size++; //数据长度+1
        modCount++; // 修改次数+1
    }
    
    private static class Node<E> {
        E item; //具体数据(王五)
        Node<E> next; // 下一个元素引用地址(null)
        Node<E> prev; // 上一个元素引用地址(李四)

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }

LinkedList 添加第二个元素的堆内存情况,如下图所示(图片来自[zhuanlan.zhihu.com/p/28101975]

从上图可知,LinkedList对象的first引用指向Node1,而Node1的item引用指向张三,Node1的next引用指向Node2,Node2的item引用指向李四,Node2的prev指向Node1,在来看添加王五,赵六;如下图所示(图片来自[zhuanlan.zhihu.com/p/28101975]
从上图可知,LinkedList底层是用的Node内部类实现的一个双向链表的数据结构;

LinkedList 和 ArrayList的比较

  • LinkedList:
  1. 底层采用的是双向链表的数据结构
  2. 查找数据时,需从头开始遍历,所以查找效率低,且不能随机访问
  3. 增删数据时,头数据的话,只需修改next属性,尾数据,只需修改prev属性,中间数据,两属性都修改,所以性能较高
  4. 线程不安全
  • ArrayList:
  1. 底层采用的是数组的数据结构
  2. 查找数据时,可以访问数组下标,支持随机访问
  3. 增删数据时,找到删除数据下标位置,置为null之后,还需将后面的元素位置移至前面,所以性能相对较低
  4. 线程不安全