Java8源码-LinkedList

前几天学习了ArrayList源码Vector源码迭代器模式在它们源码中的使用,今天开始学习LinkedList源码。参考的JDK版本为1.8。

LinkedList和ArrayList与Vector同样实现了List接口,但它执行某些操作如插入和删除元素操作比ArrayList与Vector更高效,而随机访问操作效率低。除此之外,LinkedList还添加了可以使其用作栈、队列或双端队列的方法。LinkedList在实现方面与ArrayList与Vector有哪些不同呢?为什么它插入删除操作效率高?随机访问操作效率低?它是如何用作栈、队列或双端队列的?本文将分析LinkedList的内部结构及实现原理,帮助大家更好的使用它,并一一解答上述问题。

顶部注释

Doubly-linked list implementation of the List and Deque interfaces. Implements all optional list operations, and permits all elements (including null).

第一段大意为LinkedList是List接口和Deque接口的双向链表实现。LinkedList实现了所有的列表操作,允许所有的元素(包括空元素)。

All of the operations perform as could be expected for a doubly-linked list. Operations that index into the list will traverse the list from the beginning or the end,whichever is closer to the specified index.

第二段大意为所有的操作都是在对双向链表操作。

Note that this implementation is not synchronized.
If multiple threads access a linked list concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements; merely setting the value of an element is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the list.

第三段大意为LinkedList不是线程安全的。

If no such object exists, the list should be “wrapped” using the Collections#synchronizedList Collections.synchronizedList method. This is best done at creation time, to prevent accidental unsynchronized access to the list:

 List list = Collections.synchronizedList(new LinkedList(...));

第四段大意为Collections.synchronizedList方法可以实现线程安全的操作。

The iterators returned by this class’s iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the Iterator’s own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

第五第六段大意为由iterator()和listIterator()返回的迭代器是fail-fast的。想详细了解fail-fast请参考我的另一文章Java容器源码-详解fail-fast

LinkedList类层次结构

先来看看LinkedList的定义

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

从中我们可以了解到

  • LinkedList<E>:说明它支持泛型。
  • extends AbstractSequentialList<E>
    AbstractSequentialList 继承自AbstractList,但AbstractSequentialList 只支持按次序访问,而不像 AbstractList 那样支持随机访问。这是LinkedList随机访问效率低的原因之一。
  • implements List<E>:说明它支持集合的一般操作。
  • implements Deque<E>:Deque,Double ended queue,双端队列。LinkedList可用作队列或双端队列就是因为实现了它。
  • implements Cloneable:表明其可以调用clone()方法来返回实例的field-for-field拷贝。
  • implements java.io.Serializable:表明该类是可以序列化的。

与ArrayList对比发现,LinkedList并没有实现RandomAccess,而实现RandomAccess表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。这是LinkedList随机访问效率低的原因之一。

下图是LinkedList的类结构层次图

MarkdownPhotos/master/CSDNBlogs/container/7.LinkedList/LinkedListTH.jpg

如何查看类层次结构图可以参考我写过的一篇文章:

eclipse-查看继承层次图/继承实现层次图

全局变量

/**
 * LinkedList节点个数
 */
transient int size = 0;

/**
 * 指向头节点的指针
 * Invariant: (first == null && last == null) ||
 *            (first.prev == null && first.item != null)
 */
transient Node<E> first;

/**
 * 指向尾节点的指针
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
transient Node<E> last;

关于node的详细介绍请看本文末尾的内部类—Node.java一节。Node表示链表每个节点的内部结构,包括一个数据域item,一个后置指针next,一个前置指针prev。

构造方法

接下来,看LinkedList提供的构造方法。ArrayList提供了两种构造方法。
1.构造空LinkedList。

/**
 * 构造一个空链表.
 */
public LinkedList() {
}

2.构造空LinkedList。

/**
 * 根据指定集合c构造linkedList。先构造一个空linkedlist,在把指定集合c中的所有元素都添加到linkedList中。
 *
 * @param  c 指定集合
 * @throws NullPointerException 如果特定指定集合c为null
 */
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

方法

以下几个方法是操作链表的底层方法
linkFirst(E e)

方法在表头添加指定元素e,在表头添加元素的过程如下:
MarkdownPhotos/master/CSDNBlogs/container/7.LinkedList/insetLinkFirst.jpg

源码如下:

/**
 * 在表头添加元素
 */
private void linkFirst(E e) {
    //使节点f指向原来的头结点
    final Node<E> f = first;
    //新建节点newNode,节点的前指针指向null,后指针原来的头节点
    final Node<E> newNode = new Node<>(null, e, f);
    //头指针指向新的头节点newNode 
    first = newNode;
    //如果原来的头结点为null,更新尾指针,否则使原来的头结点f的前置指针指向新的头结点newNode
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}
linkLast(E e)

方法在表尾插入指定元素e。添加元素的过程如下:
MarkdownPhotos/master/CSDNBlogs/container/7.LinkedList/insetLinkLast.jpg

/**
 * 在表尾插入指定元素e
 */
void linkLast(E e) {
    //使节点l指向原来的尾结点
    final Node<E> l = last;
    //新建节点newNode,节点的前指针指向l,后指针为null
    final Node<E> newNode = new Node<>(l, e, null);
    //尾指针指向新的头节点newNode
    last = newNode;
    //如果原来的尾结点为null,更新头指针,否则使原来的尾结点l的后置指针指向新的头结点newNode
    if (l == null)
        first = newNode;
    else
        l.n
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值