public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable {
PriorityQueue只實現了AbstractQueue抽象類也就是實現了Queue接口。java
//默認初始化容量 private static final int DEFAULT_INITIAL_CAPACITY = 11; //經過徹底二叉樹(complete binary tree)實現的小頂堆 transient Object[] queue; private int size = 0; //比較器 private final Comparator<? super E> comparator; //隊列結構被改變的次數 transient int modCount = 0;
根據transient Object[] queue; 的英文註釋:node
Priority queue represented as a balanced binary heap: the two children of queue[n] are queue[2*n+1] and queue[2*(n+1)].數組
咱們能夠知道PriorityQueue內部是經過徹底二叉樹(complete binary tree)實現的小頂堆(注意:這裏咱們定義的比較器爲越小優先級越高)實現的。數據結構
優先隊列PriorityQueue內部是經過堆實現的。堆分爲大頂堆和小頂堆:
多線程
leftNo = parentNo*2+1 rightNo = parentNo*2+2 parentNo = (nodeNo-1)/2
經過上面的公式能夠很輕鬆的根據某個節點計算出父節點和左右孩子節點。dom
其實add()方法內部也是調用了offer().下面是offer()的源碼:ide
public boolean offer(E e) { //不容許空 if (e == null) throw new NullPointerException(); //modCount記錄隊列的結構變化次數。 modCount++; int i = size; //判斷 if (i >= queue.length) //擴容 grow(i + 1); //size加1 size = i + 1; if (i == 0) queue[0] = e; else //不是第一次添加,調整樹結構 siftUp(i, e); return true; }
咱們能夠知道:函數
private void grow(int minCapacity) { int oldCapacity = queue.length; // Double size if small; else grow by 50% //若是queue的大小小於64擴容爲原來的兩倍再加2,反之擴容爲原來的1.5倍 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); }
private void siftUp(int k, E x) { //比較器非空狀況 if (comparator != null) siftUpUsingComparator(k, x); else siftUpComparable(k, x); } //比較器非空狀況 @SuppressWarnings("unchecked") private void siftUpUsingComparator(int k, E x) { while (k > 0) { //利用堆的特性:parentNo = (nodeNo-1)/2 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; }
上面的源碼中可知:線程
下面是畫的是向一個優先隊列中添加(offer)一個元素過程的草圖,以便理解:
日誌
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; }
刪除元素,也得調整結構以維持優先隊列內部數據結構爲:堆
下面是一段簡單的事列代碼,演示了天然排序和自定義排序的狀況:
package com.good.good.study.queue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Comparator; import java.util.PriorityQueue; import java.util.Random; /** * @author monkjavaer * @version V1.0 * @date 2019/6/16 0016 10:22 */ public class PriorityQueueTest { /**日誌記錄*/ private static final Logger logger = LoggerFactory.getLogger(PriorityQueueTest.class); public static void main(String[] args) { naturalOrdering(); personOrdering(); } /** * 天然排序 */ private static void naturalOrdering(){ PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(); Random random = new Random(); int size = 10; for (int i =0;i<size;i++){ priorityQueue.add(random.nextInt(100)); } for (int i =0;i<size;i++){ logger.info("第 {} 次取出元素:{}",i,priorityQueue.poll()); } } /** * 自定義排序規則,根據人的年齡排序 */ private static void personOrdering(){ PriorityQueue<Person> priorityQueue = new PriorityQueue<>(new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.getAge()-o2.getAge(); } }); Random random = new Random(); int size = 10; for (int i =0;i<size;i++){ priorityQueue.add(new Person(random.nextInt(100))); } while (true){ Person person = priorityQueue.poll(); if (person == null){ break; } logger.info("取出Person:{}",person.getAge()); } } }
優先隊列PriorityQueue經常使用於調度系統優先處理VIP用戶的請求。多線程環境下咱們須要使用PriorityBlockingQueue來處理此種問題,以及須要考慮隊列數據持久化。