LinkedList原始碼解讀

qixiaobo發表於2018-01-05

title: LinkedList原始碼解讀 tags:

  • JCF
  • LinkedList
  • Queue
  • Deque categories: jcf date: 2017-09-30 11:43:57

List中除了ArrayList我們最常用的就是LinkedList了。

LInkedList與ArrayList的最大區別在於元素的插入效率和隨機訪問效率

可以參考最熟悉的ArrayList

慣例發出類圖

161610_jJM9_871390.png

大概出乎大家意料的是LinkedList除了是List還是Queue。

Java 在1.5引入了Queue容器在1.6引入了Deque容器

我們看一下Deque具體多了那些介面

161622_d6iA_871390.png

A collection designed for holding elements prior to processing. Besides basic Collection operations, queues provide additional insertion, extraction, and inspection operations. Each of these methods exists in two forms: one throws an exception if the operation fails, the other returns a special value (either null or false, depending on the operation). The latter form of the insert operation is designed specifically for use with capacity-restricted Queue implementations; in most implementations, insert operations cannot fail.

  Throws exception Returns special value
Insert add(e) offer(e)
Remove remove() poll()
Examine element() peek()

Deque在Queue的基礎上增加了雙端操作的方法

A linear collection that supports element insertion and removal at both ends. The name deque is short for "double ended queue" and is usually pronounced "deck". Most Dequeimplementations place no fixed limits on the number of elements they may contain, but this interface supports capacity-restricted deques as well as those with no fixed size limit.

This interface defines methods to access the elements at both ends of the deque. Methods are provided to insert, remove, and examine the element. Each of these methods exists in two forms: one throws an exception if the operation fails, the other returns a special value (either null or false, depending on the operation). The latter form of the insert operation is designed specifically for use with capacity-restricted Deque implementations; in most implementations, insert operations cannot fail.

The twelve methods described above are summarized in the following table:

  First Element (Head) Last Element (Tail)
  Throws exception Special value Throws exception Special value
Insert addFirst(e) offerFirst(e) addLast(e) offerLast(e)
Remove removeFirst() pollFirst() removeLast() pollLast()
Examine getFirst() peekFirst() getLast() peekLast()

This interface extends the Queue interface. When a deque is used as a queue, FIFO (First-In-First-Out) behavior results. Elements are added at the end of the deque and removed from the beginning. The methods inherited from the Queue interface are precisely equivalent to Deque methods as indicated in the following table:

Queue Method Equivalent Deque Method
add(e) addLast(e)
offer(e) offerLast(e)
remove() removeFirst()
poll() pollFirst()
element() getFirst()
peek() peekFirst()

Deques can also be used as LIFO (Last-In-First-Out) stacks. This interface should be used in preference to the legacy Stack class. When a deque is used as a stack, elements are pushed and popped from the beginning of the deque. Stack methods are precisely equivalent to Deque methods as indicated in the table below:

Stack Method Equivalent Deque Method
push(e) addFirst(e)
pop() removeFirst()
peek() peekFirst()

Note that the peek method works equally well when a deque is used as a queue or a stack; in either case, elements are drawn from the beginning of the deque.

佇列(主要是阻塞 有界/無界 佇列)和雙向佇列的出現為生產者消費者模式提供了極好的實現(設想如果需要各種喚醒等操作,Java也不會有今天這種規模的生態圈)

來看一下LinkedList的建構函式

    /**
     * Constructs an empty list.
     */
    public LinkedList() {
    }
     
    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param  c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
複製程式碼

基本上非常輕量級沒有太多的初始化工作,並且由於並非使用陣列來做實現因此也不存在一些負載因子來影響效能的引數

基本上就是較為經典的連結串列實現的過程

    /**
     * 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++;
    }
複製程式碼

基於雙向佇列封裝了兩端特殊的API 同時由於實現了Deque因此也提供了一個反序的迭代器(支援反序迭代)

    /**
     * @since 1.6
     */
    public Iterator<E> descendingIterator() {
        return new DescendingIterator();
    }
     
    /**
     * Adapter to provide descending iterators via ListItr.previous
     */
    private class DescendingIterator implements Iterator<E> {
        private final ListItr itr = new ListItr(size());
        public boolean hasNext() {
            return itr.hasPrevious();
        }
        public E next() {
            return itr.previous();
        }
        public void remove() {
            itr.remove();
        }
    }
複製程式碼

由於支援雙向迭代,因此資料結構必須是雙向連結串列,包含next指標和prev指標

    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;
        }
    }
複製程式碼

由於這種結構因此請不要使用如下迭代方式

    for(int i = 0 ; i < size ; i++) {
     system.out.println(list.get(i));
    }
複製程式碼

具有良好的插入和刪除特性

這也是上篇文章中利用LinkedList來作為效能改善的原因

相關文章