PriorityQueue源碼分析

PriorityQueue是從JDK1.5開始提供的新的數據結構接口,它是一種基於 優先級堆的極大優先級隊列。優先級隊列是不一樣於先進先出隊列的另外一種隊列。每次從隊列中取出的是具備最高優先權的元素。若是不提供Comparator的話,優先隊列中元素默認按天然順序排列,也就是數字默認是小的在隊列頭,字符串則按字典序排列,也能夠根據 Comparator 來指定,這取決於使用哪一種構造方法。優先級隊列不容許 null 元素。依靠天然排序的優先級隊列還不容許插入不可比較的對象(這樣作可能致使 ClassCastException)

優先級隊列是無界的,可是有一個內部容量,控制着用於存儲隊列元素的數組大小。它一般至少等於隊列的大小。隨着不斷向優先級隊列添加元素,其容量會自動增長。無需指定容量增長策略的細節。 java

咱們從源碼的角度來看看PriorityQueue是如何實現的。 node

建堆:

PriorityQueue內部的數組聲明以下: api

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.
     */
 private transient Object[] queue;

咱們發現queue前面使用了關鍵字transient,這是爲何呢? 數組

PriorityQueue的默認長度爲11,並且PriorityQueue是會擴容的 安全

/**
     * Increases the capacity of the array.
     *
     * @param minCapacity the desired minimum capacity
     */
    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);

因此大多數狀況下,真實數據的大小都小於數組的queue實際大小,若是將整個queue都序列化,那有不少空間是浪費的。因此PriorityQueue本身實現了writeObject與readObject來提升性能。(關於對象的序列化請查看這裏 數據結構

PriorityQueue初始化過程和最大堆的建堆過程基本上同樣的(想了解建堆過程請查看排序篇的堆排),從有子節點的最靠後元素開始往前,每次都調用siftDown方法來調整。這個過程也叫作heapify。 oop

固然,只有當你想將現有的數據轉成PriorityQueue時才須要建堆,在空的PriorityQueue中add新元素時只是一個調整的過程 性能

/**
     * Creates a {@code PriorityQueue} containing the elements in the
     * specified collection.  If the specified collection is an instance of
     * a {@link SortedSet} or is another {@code PriorityQueue}, this
     * priority queue will be ordered according to the same ordering.
     * Otherwise, this priority queue will be ordered according to the
     * {@linkplain Comparable natural ordering} of its elements.
     *
     * @param  c the collection whose elements are to be placed
     *         into this priority queue
     * @throws ClassCastException if elements of the specified collection
     *         cannot be compared to one another according to the priority
     *         queue's ordering
     * @throws NullPointerException if the specified collection or any
     *         of its elements are null
     */
    @SuppressWarnings("unchecked")
    public PriorityQueue(Collection<? extends E> c) {
        if (c instanceof SortedSet<?>) {
            SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
            this.comparator = (Comparator<? super E>) ss.comparator();
            initElementsFromCollection(ss);
        }
        else if (c instanceof PriorityQueue<?>) {
            PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
            this.comparator = (Comparator<? super E>) pq.comparator();
            initFromPriorityQueue(pq);
        }
        else {
            this.comparator = null;
            initFromCollection(c);
        }
    }
PriorityQueue支持將Collection類型直接轉成 PriorityQueue。

建堆過程: ui

private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }

private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }

private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
        int half = size >>> 1;        // loop while a non-leaf
        while (k < half) {
            int child = (k << 1) + 1; // assume left child is least
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
                c = queue[child = right];
            if (key.compareTo((E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = key;
    }

    private void siftDownUsingComparator(int k, E x) {
        int half = size >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }

添加新元素的過程以下: this

public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
        if (i == 0)
            queue[0] = e;
        else
            siftUp(i, e);
        return true;
    }

每次添加新的元素進來其實是在數組的最後面增長。在增長這個元素的時候就有了判斷數組長度和調整的一系列動做。等這些動做調整完以後就要進行siftUp方法調整。這樣作是爲了保證堆原來的性質。

其餘:

1. 此實現不是同步的。不是線程安全的。若是多個線程中的任意線程從結構上修改了列表, 則這些線程不該同時訪問 PriorityQueue 實例,這時請使用線程安全的PriorityBlockingQueue 類。

2. 此實現爲offer、poll、和 add 方法提供 O(logn) 時間;爲 remove(Object) 和 contains(Object) 方法提供O(n)時間;爲檢索方法(peek、element 和 size)提供O(1)。

3. 方法iterator()中提供的迭代器並不保證以有序的方式遍歷優先級隊列中的元素。由於最小堆只能保證根結點是最小,不能保證總體都有序。

Reference:

1. http://cuisuqiang.iteye.com/blog/2019157

2. http://shmilyaw-hotmail-com.iteye.com/blog/1827136

3. http://zhidao.baidu.com/link?url=jJlf0fLjuv6Y0OjvvQqLo46PhAMdVTHau9NkmHpBYgDBDAtlBnUJrtUfDxS22ftpeR9su6m6n85K6X8W7FpvO_

相關文章
相關標籤/搜索