LinkedList源码深度解析

263 阅读3分钟

本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看 活动链接

承接上一篇ArrayList,我们继续来谈一谈关于List家族的代码,今天我们要分析的是LinkedList,作为家族的一员LinkedList和ArrayList分别被用于不同的场景,它们在各自的领域发光发热,说到这其实我觉得应该抛出本文的第一个问题了,在什么情况下我们该用ArrayList什么情况下我们用LinkedList?

数据结构

我们都知道ArrayList的数据结构是数组,那么LinkedList的数据结构又是什么呢?我们可以从源码开头的注释中得到答案,注释是这么写的:All of the operations perform as could be expected for a doubly-linked list. 简单来说所有表现和双链表一致,代码结构如下所示。

// 初始容量
transient int size = 0;
// 指向前一个的指针
transient Node<E> first;
// 指向后一个的指针
transient Node<E> last;

可以看出存在2个指针分别指向first和last的节点,这是一个典型的双向链表的结构,至于transient这个参数我们后续在多线程的基础知识章节会进行补充扩展。

image.png

上面这个图就很好的描述了这个结构。

关键源码

其实LinkedList的实现没啥好多讨论的,就是一个比较完善的双链表操作的类,这里我们重点看一下新增和查询。

新增

这里的新增删除和修改其实就是对双链表执行新增删除修改操作。

public boolean add(E e) {
    linkLast(e);
    return true;
}

上面是新增代码,依赖于linkLast实现。实现原理非常简单,就是在链表的尾巴加一个数据,并用指针关联起来。

void linkLast(E e) {
  	// 拿到尾巴的指针
    final Node<E> l = last;
    // 根据数据新建指针
    final Node<E> newNode = new Node<>(l, e, null);
    // 新节点接入指针
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    // 这里就是LinkedList和arrayList不支持并非的原因。
    modCount++;
}

之所以不支持并发的原因,在于所有操作都会产生modCount的变化,假如多线程在操作的时候modCount变化会变的无规律,而在ArrayList和LinkedList中在做某些操作之前会对modCount的值进行校验,如果校验不通过就会抛出ConcurrentModificationException异常。

查询

Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

上图是一个根据index找到对应节点的方法,可以很直白的看出一个问题,那就是我查到一个节点所耗费的精力是非常大的,虽然已经做到的二分(index > 1/2 * size,就从尾指针开始迭代),但是这样的效率还是太低了,假如我有1w个节点,而我要找的结点在5001位呢?所以从这里我们可以看出LinkedList不适合存储需要频繁查找的数据

最后我可以来回答之前文章开头所提出的问题了,LinkedList的数据结构是链表,链表的优势是做增删很快尤其是双向链表,而ArrayList是数组,数组的优势就是查找很快,而数组的劣势是新增和删除很麻烦。所以总结一下:

  • ArrayList使用在查询比较多,但是插入和删除比较少的情况。
  • LinkedList使用在查询比较少而插入和删除比较多的情况。

总结

到此为止List家族中线程不安全的两兄弟已经说完了,接下来的文章就是分析Vector了,这是一个线程安全的集合框架,其实和ArrayList,LinkedList相差无几唯一的区别就是Vector用上了synchronized关键字来保障线程安全,这一点会在线程系列文章中做专门叙述,文章就到这里,感谢阅读。