之前分析过ArrayBlockingQueue,它的特点是
1 有限,不会扩容
2 严格的先进先出
今天要介绍的PriorityBlockingQueue,正好突破了这两点,第一它是无限的,第二它是根据元素的大小进行排序的的队列
排序所使用的算法是堆排序,而且是小顶堆,也就是说值越小越优先
补充一下,默认确实是小顶堆,但是构造方法提供了比较器,也可以实现大顶堆。
public PriorityQueue(Comparator<? super E> comparator) { this(DEFAULT_INITIAL_CAPACITY, comparator); }
PriorityQueue<Integer> queue = new PriorityQueue<>((num1, num2) -> num2 - num1);
一 入队
public boolean offer(E e) { if (e == null) throw new NullPointerException(); final ReentrantLock lock = this.lock; lock.lock(); int n, cap; Object[] array; while ((n = size) >= (cap = (array = queue).length)) tryGrow(array, cap);//扩容,扩容的条件是size已经到头了 这个和hashmap可不一样 try { Comparator<? super E> cmp = comparator; if (cmp == null) siftUpComparable(n, e, array);//堆调整,n始终是当前的size else siftUpUsingComparator(n, e, array, cmp); size = n + 1;//有lock包裹着不用加volatile notEmpty.signal();//唤醒等待线程 } finally { lock.unlock(); } return true; }
private static <T> void siftUpComparable(int k, T x, Object[] array) { Comparable<? super T> key = (Comparable<? super T>) x; while (k > 0) { int parent = (k - 1) >>> 1; Object e = array[parent]; if (key.compareTo((T) e) >= 0) break; array[k] = e; k = parent; } array[k] = key; }
入队就是从下到上,从右到左的找该元素应该所在的位置
二 出队列
public E poll() { final ReentrantLock lock = this.lock; lock.lock(); try { return dequeue(); } finally { lock.unlock(); } }
private E dequeue() { int n = size - 1; if (n < 0) return null; else { Object[] array = queue; E result = (E) array[0];//每次出队都是第0个 E x = (E) array[n];//把当前的最后那个取出来缓存起来 array[n] = null; Comparator<? super E> cmp = comparator; if (cmp == null) siftDownComparable(0, x, array, n);//从0开始往下走,逐渐调整 else siftDownUsingComparator(0, x, array, n, cmp); size = n; return result; } }
private static <T> void siftDownComparable(int k, T x, Object[] array, int n) { if (n > 0) { Comparable<? super T> key = (Comparable<? super T>)x; int half = n >>> 1; // loop while a non-leaf while (k < half) { int child = (k << 1) + 1; // assume left child is least Object c = array[child]; int right = child + 1; if (right < n && ((Comparable<? super T>) c).compareTo((T) array[right]) > 0) c = array[child = right]; if (key.compareTo((T) c) <= 0) break; array[k] = c; k = child; } array[k] = key; } }
出队列就是把堆头取出来,然后进行堆调整。