天天看點

隊列Queue、雙端隊列Deque源碼分析及底層原理隊列Queue優先隊列PriorityQueue雙端隊列Deque

文章目錄

  • 隊列Queue
  • 優先隊列PriorityQueue
  • 雙端隊列Deque

隊列Queue

在Java的标準庫中,隊列接口Queue定義了以下幾個方法:

  • int size():擷取隊列長度;
  • boolean add(E)/boolean offer(E):添加元素到隊尾;
  • E remove()/E poll():擷取隊首元素并從隊列中删除;
  • E element()/E peek():擷取隊首元素但并不從隊列中删除。

注意到添加、删除和擷取隊列元素總是有兩個方法,這是因為在添加或擷取元素失敗時,這兩個方法的行為是不同的。我們用一個表格總結如下:

隊列Queue、雙端隊列Deque源碼分析及底層原理隊列Queue優先隊列PriorityQueue雙端隊列Deque

隊列的實作類:LinkedList

優先隊列PriorityQueue

PriorityQueue和Queue的差別在于,它的出隊順序與元素的優先級有關,對PriorityQueue調用remove()或poll()方法,傳回的總是優先級最高的元素。

要使用PriorityQueue,我們就必須給每個元素定義“優先級”。

要實作放入優先隊列元素的優先級,方法為:

  • 放入PriorityQueue的元素,必須實作Comparable接口,PriorityQueue會根據元素的排序順序決定出隊的優先級。
  • PriorityQueue允許我們提供一個Comparator對象來判斷兩個元素的順序

可以這樣了解優先隊列:

  • 始終建構小頂堆,即堆頂的元素按排序算法始終最小(傳回-1).
  • 排序算法是這樣的:傳回0,表示相等,傳回整數表示大于,傳回複數表示小于
  • 由于優先隊列即是對堆的實作,其底層也是基于動态數組加上擴容機制,隻不過加了個堆排序

源碼

public class PriorityQueue<E> extends AbstractQueue<E>
    implements java.io.Serializable {


    private static final long serialVersionUID = -7720805057305804111L;

//動态數組預設大小
    private static final int DEFAULT_INITIAL_CAPACITY = 11;


    /**
     * Priority queue represented as a balanced binary heap: the two
     * children of queue[n] are queue[2*n+1] and queue[2*(n+1)].  The
     * priority queue is ordered by comparator, or by the elements'
     * natural ordering, if comparator is null: For each node n in the
     * heap and each descendant d of n, n <= d.  The element with the
     * lowest value is in queue[0], assuming the queue is nonempty.
     */
    transient Object[] queue; // non-private to simplify nested class access


    /**
     * The number of elements in the priority queue.
     */
    int size;


    /**
     * The comparator, or null if priority queue uses elements'
     * natural ordering.
     */
    private final Comparator<? super E> comparator;


    transient int modCount;     // non-private to simplify nested class access

    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }

    public PriorityQueue(int initialCapacity) {
        this(initialCapacity, null);
    }


    public PriorityQueue(Comparator<? super E> comparator) {
        this(DEFAULT_INITIAL_CAPACITY, comparator);
    }
           

動态擴容

private void grow(int minCapacity) {
    int oldCapacity = queue.length;
    // Double size if small; else grow by 50%
    int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                     (oldCapacity + 2) :
                                     (oldCapacity >> 1));
    // overflow-conscious code
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    queue = Arrays.copyOf(queue, newCapacity);
}
           

雙端隊列Deque

Java集合提供了接口Deque來實作一個雙端隊列,它的功能是:

  • 既可以添加到隊尾,也可以添加到隊首;
  • 既可以從隊首擷取,又可以從隊尾擷取。

我們來比較一下Queue和Deque出隊和入隊的方法:

隊列Queue、雙端隊列Deque源碼分析及底層原理隊列Queue優先隊列PriorityQueue雙端隊列Deque

對于添加元素到隊尾的操作,Queue提供了add()/offer()方法,而Deque提供了addLast()/offerLast()方法。添加元素到對首、取隊尾元素的操作在Queue中不存在,在Deque中由addFirst()/removeLast()等方法提供。

注意到Deque接口實際上擴充自Queue:

public interface Deque<E> extends Queue<E> {
    ...
}
           

是以,Queue提供的add()/offer()方法在Deque中也可以使用,但是,使用Deque,最好不要調用offer(),而是調用offerLast():

Deque是一個接口,它的實作類有ArrayDeque和LinkedList。

繼續閱讀