【源碼系列-2】PriorityQueue

  • PriorityQueue,我用過的場景是用來構建大小堆。
  • PriorityQueue的排序是在創建時就制定的Comparable或Comparator。
  • PriorityQueue不允許添加null元素。
  • 如果隊列中插入Comparable或Comparator無法比較的元素時,會拋出ClassCastException異常。
  • 隊列頭是隊列的最後一個元素。如果最後一個值有多個元素的話,隊列頭是任意一個元素。
  • 隊列可通過poll,remove,peek,element方法獲取隊列頭,即隊列的最後一個元素。
  • PriorityQueue的長度沒有限制,內部變量capacity用於管理隊列長度,當添加元素時,容量會自動增加。
  • PriorityQueue不是同步的,如果一個線程在修改PriorityQueue的話,其他線程無法獲取該PriorityQueue。在這種情況下,考慮線程安全的類:java.util.concurrent.PriorityBlockingQueue
  • 時間複雜度是O(log(n))的方法:offer,poll,remove(),add
  • 時間複雜度是O(n)的方法:remove(Object),contains(Object)
  • 時間複雜度是O(1)的方法:peek,element,size

PriorityQueue實現了java.io.Serializable接口,指定了serialVersionUID

PriorityQueue聲明瞭默認的容量DEFAULT_INITIAL_CAPACITY = 11;

transient關鍵字無法被序列化
final關鍵字:

  • 修飾類:該類不可以被繼承
  • 修飾方法:該方法不可以被重寫
  • 修飾變量:該變量不可以被修改
  • Comparator用private final 修飾,使得Comparator只可以在初始化時被指定,且不可以在其他時候修改。
    private final Comparator<? super E> comparator;

  • 在初始化優先隊列的容量時,最小是1,否則拋出IllegalArgumentException。

public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }
  • PriorityQueue可通過Collection的實現類構建,前提是實現類是
    SortedSet的子類或者是PriorityQueue的實例,也就是本身就有comparator的實例,否則無法創建。

  • 創建實例時,將Collection的實現類使用toArray()轉換爲數組,並遍歷,判斷有沒有null元素。

  • 優先隊列的最大size如下:
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    以爲部分虛擬機要在數組頭存儲東西,所以爲了避免爆內存-8.

  • PriorityQueue實現的基礎是平衡二叉堆,存儲在數組中,元素queue[n] 的左孩子和右孩子分別爲queue[2n+1] 、queue[2(n+1)]

  • 因爲上述存儲方式,PriorityQueue頭就是0位元素,所以返回數組的第0位。
    public E peek() { return (size == 0) ? null : (E) queue[0];}

/>>>表示無符號右移,也叫邏輯右移,即若該數爲正,則高位補0,而若該數爲負數,則右移後高位同樣補0

  • 添加元素時,用到offer函數,offer函數調用siftUpUsingComparator函數,siftUpUsingComparator函數的k是添加該元素之後的隊列的長度,x是該元素,如果想在第k位存儲元素,則該元素的父結點爲m位的元素,2*m+1=k, m=(k- 1)/2 ,如果該元素大於等於父元素,則直接保存,否則把父元素寫在當前的位置上,再找當前元素的父元素,因此offer的時間複雜度是O(log(n))。

如果調用compare方法大於0,就把前一個數和後一個數交換,也就是把大的數放後面了

private void siftUpUsingComparator(int k, E x) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (comparator.compare(x, (E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }
  • poll方法直接把最後一位賦爲空,並傳入最後一位元素到siftDown函數中。
public E poll() {
    if (size == 0)
        return null;
    int s = --size;
    modCount++;
    E result = (E) queue[0];
    E x = (E) queue[s];
    queue[s] = null;
    if (s != 0)
        siftDown(0, x);
    return result;
}
  • siftDown方法調用siftDownComparable函數,對size除2,size是原來的size0-1之後的結果,half=(size0-1)/2,即half所在的元素是第size0位的父結點。k是0,如果k小於half,則證明還沒有把父結點填充上,需要獲取k的左孩子c,如果左孩子大於右孩子,則把child指向右孩子,並把有孩子賦值給c用c去替換父節點,直到替換到跟節點。
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;
}
  • contains要遍歷一邊數組,所以是O(n)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章