Java容器系列-LinkedList 源码分析

LinkedList 做为 List 的另外一种实现,也很是的经典。与 ArrayList 不一样,LinkedList 底层使用的是双向链表来实现的,具体类图以下:java

相比于 ArrayList,LinkedList 继承了 AbstractSequentialList 类,并且实现了 Deque 接口,RandomAccess 接口就被没有实现。微信

但在实际的使用当中,LinkedList 使用的并无 ArrayList 多,LinkedList 能够被当作队列和栈来使用,可是 BlockingQueue 使用的比它更为普遍,由于通常使用队列的地方都会涉及到比较高的并发,在高并发的状况下,BlockingQueue 比 LinkedList 更好用,BlockingQueue 之后会写专门的文章来介绍。数据结构

本文基于 JDK1.8并发

成员变量

LinkedList 的结构比 ArrayList 更简单,核心的成员变量以下,size 记录当前元素的个数,first 指向头结点,last 指向尾节点。dom

transient int size = 0;
transient Node<E> first;
transient Node<E> last;
复制代码

Node 的代码以下,经过泛型来存储具体的元素,每一个节点均可以获取前一个或者后一个节点,因此 LinkedNode 底层数据结构是双向链表函数

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}
复制代码

由于底层使用的双向链表,因此理论上来讲 LinkedList 的容量是没有限制的,天然也没有了扩容的过程。高并发

实例化过程

LinkedList 的实例化过程也相对简单,只提供了两个构造函数。源码分析

一个不带任何参数,也不须要作任何的数据初始化,头结点和尾节点的初始化都放在添加第一个元素的时候。post

public LinkedList() {
}
复制代码

第二个构造函数接收一个 Collection 类型的对象,会把对象中的全部元素都添加到当前 LinkedList 对象中。this

public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
 }
复制代码

具体实现

与 ArrayList 相比,LinkedList 有以下特色:

  • 不能随机访问
  • 容量无限
  • 能够用做队列双向队列

由于 LinkedList 没有实现 RandomAccess 接口,再加上自己底层的数据结构是双向链表,因此对链表中的元素不能随机访问,只能按照顺序访问。

并且对于链表来讲,元素时能够无限扩展(理论上)的,因此 LinkedList 的容量也没有上限。

从 JDK1.6 开始,LinkedList 实现了 Deque 接口,这就代表 LinkedList 能够用做队列或者双向队列

增删改查方法

List 中有的方法,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++;
    modCount++;
}
复制代码

上面是把元素添加到链表的尾部,由于 LinkedList 还能够被用做队列和栈,所以还提供了从头部添加元素的方法:

private void linkFirst(E e) {
    final Node<E> f = first;
    final Node<E> newNode = new Node<>(null, e, f);
    first = newNode;
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}
复制代码

既然有添加元素的操做,也有删除元素的操做,代码以下:

private E unlinkLast(Node<E> l) {
    final E element = l.item;
    final Node<E> prev = l.prev;
    l.item = null;
    l.prev = null;
    last = prev;
    if (prev == null)
        first = null;
    else
        prev.next = null;
    size--;
    modCount++;
    return element;
}
复制代码

清空 LinkedList 就是把全部的元素置为 null:

public void clear() {
    for (Node<E> x = first; x != null; ) {
        Node<E> next = x.next;
        x.item = null;
        x.next = null;
        x.prev = null;
        x = next;
    }
    first = last = null;
    size = 0;
    modCount++;
}
复制代码

从以上代码能够发现,LinkedList 的操做都是对链表的操做。

用做队列和栈

作为普通队列时,能够在队列中进行入队和出队的操做。从队列头部获取一个元素,可是不删除元素 peek():

public E peek() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}
复制代码

从队列头部获取一个元素并删除,poll():

public E poll() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}
复制代码

在队列的尾部添加一个元素,offer():

public boolean offer(E e) {
    return add(e);
 }
复制代码

普通队列只能够在一端入队,在另外一端出队,可是对于双向队列,能够在两端执行入队和出队操做。

因此在在做为双向队列时,拿 peek 操做来讲便可以 peekFirst() 也能够 peekLast()。其余的操做例如 offer、poll 一样相似。

LinkedList 中还有 push()pop() 操做。被当作栈使用时,只须要对头部节点进行操做就行。

入栈操做:

public void push(E e) {
    addFirst(e);
}
复制代码

出栈操做:

public E pop() {
    return removeFirst();
}
复制代码

其余功能

LinkedList 中也实现了 ListIterator 和 Spliterator 接口。

因此 LinkedList 也能够从两端进行遍历。在遍历的时候,一样也有 fail-fast 机制来检查遍历的过程中,容器中的元素是否被修改。

在实现 Spliterator 接口以后,也能够对容器中的元素进行分段,而后同时让多个线程同时进行处理,提升处理效率。分割 LinkedList 的代码以下:

LinkedList<Integer> list = new LinkedList<>();

    for (int i = 0; i < 20; i++) {
        list.add(i);
    }

    Spliterator<Integer> splitor = list.spliterator();
    Spliterator<Integer> s1 = splitor.trySplit();
    Spliterator<Integer> s2 = s1.trySplit();

    System.out.println(s1.estimateSize()); // 10
    System.out.println(s2.estimateSize()); // 10
复制代码

(完)

原文

相关文章

关注微信公众号,聊点其余的

相关文章
相关标签/搜索