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来处理此种问题,以及须要考虑队列数据持久化。