Java8 LinkedList源码简析

    xiaoxiao2021-03-25  69

      网上的LinkedList源码分析我看到的基本应该都不是Java8的(我猜现在应该挺多了,文章是从我原来的博客转移过来的,一年多前写的了)。   先看下这个类的介绍:

    /** * Doubly-linked list implementation of the {@code List} and {@code Deque} * interfaces. Implements all optional list operations, and permits all * elements (including {@code null}). * * <p>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. * * <p><strong>Note that this implementation is not synchronized.</strong> * If multiple threads access a linked list concurrently, and at least * one of the threads modifies the list structurally, it <i>must</i> 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. * * If no such object exists, the list should be "wrapped" using the * {@link Collections#synchronizedList Collections.synchronizedList} * method. This is best done at creation time, to prevent accidental * unsynchronized access to the list:<pre> * List list = Collections.synchronizedList(new LinkedList(...));</pre> * * <p>The iterators returned by this class's {@code iterator} and * {@code listIterator} methods are <i>fail-fast</i>: if the list is * structurally modified at any time after the iterator is created, in * any way except through the Iterator's own {@code remove} or * {@code add} methods, the iterator will throw a {@link * 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. * * <p>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 {@code ConcurrentModificationException} on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this * exception for its correctness: <i>the fail-fast behavior of iterators * should be used only to detect bugs.</i> * * <p>This class is a member of the * <a href="{@docRoot}/../technotes/guides/collections/index.html"> * Java Collections Framework</a>. * * @author Josh Bloch * @see List * @see ArrayList * @since 1.2 * @param <E> the type of elements held in this collection */

      双链表实现,支持List,Deque,不同步,fail-fast behavior。   fail-fast behavior指面对并发的修改,迭代器很快会挂掉(ConcurrentModificationException),避免在将来不确定的时间发生不确定的行为,不过上面也说了,它只是尽力而为(没错,和网络层提供的尽力而为的服务意思差不多),别指望它来保证你程序的正确性。      类定义:

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

      如果你对Cloneable接口或Java序列化不知道,可以去了解下相关知识。      Java8的变化,头结点去除了,不过有指向第一个和最后一个节点的引用,还有个记录链表节点数量的变量:

    transient int size = 0; /** * Pointer to first node. * Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */ transient Node<E> first; /** * Pointer to last node. * Invariant: (first == null && last == null) || * (last.next == null && last.item != null) */ transient Node<E> last;

      可以看到上面三个成员变量都加了transient关键字修饰,我们知道transient关键字的作用是让这个成员变量不能被序列化,那这里为什么要这样做?   我暂时也不是很清楚,可以看看Effective Java第11章第75条。。。      维护指向第一个和最后一个节点两个引用有很多好处,下面说一些:   根据索引查找某一节点时,可以根据索引值决定是从前开始遍历效率高,还是从后开始遍历效率高,源码中的体现如下:

    /** * Returns the (non-null) Node at the specified element index. */ 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; } }

      方便的实现头插法,尾插法:

    /** * Links e as first element. */ 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++; } /** * Links e as last element. */ 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++; }

      方便的从头尾插入,移除元素(就不贴源码了,要不然有凑字数的嫌疑。。。)。      构造函数也有些变化,以前第一个构造函数是用来帮助构造循环链表的,现在空空如也(因为维护了指向第一个和最后一个节点两个引用,所以可能不需要循环链表了吧):

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

            具体节点类(以前叫Entry):

    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; } }

      至于增删查改之类的肯定有的啦,这些就不说了,可以看我这篇文章实现的一个简陋版:   Java实现单链表的一些操作   因为支持头插法,尾插法增加节点。

    /** * Inserts the specified element at the beginning of this list. * * @param e the element to add */ public void addFirst(E e) { linkFirst(e); } /** * Appends the specified element to the end of this list. * * <p>This method is equivalent to {@link #add}. * * @param e the element to add */ public void addLast(E e) { linkLast(e); }

      所以你要用它实现栈和队列的特性都可以。   默认尾插法:

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

      这次先写到这里吧。

    转载请注明原文地址: https://ju.6miu.com/read-34965.html

    最新回复(0)