Priorityqueue是Java7后加入的优先级队列,继承自AbstractQueue,而AbstractQueue,实现了Queue接口,也就是说Priorityqueue是实现了Queue接口的,带有优先级的队列。
内部实现上采用的是最小堆,即每次取出优先级最小的元素。相对来说实现上比较简单,下面简单分析一下。
一、基本属性与构造函数
1 | public class PriorityQueue<E> extends AbstractQueue<E> |
1、私有属性
- DEFAULT_INITIAL_CAPACITY:静态常量,也就是默认的初始化容量,11
- transient Object[] queue:存储元素的数组,Object类型的
- private int size:表示元素数量
- comparator:比较器,用于优先级的比较,默认情况下是最小堆
- transient int modCount:修改的次数,用于fast-fail
2、构造函数
- 前三个构造器实际上都是调用第四个构造器,即制定初始化容量与比较器,而构造器4也很简单,就是根据给定的初始容量和构造器初始化数组和绑定比较器
- 后面的三个构造器用于指定一个已有的集合初始化,根据输入类型的不同,分为以下几种:
- Collection:再继续判断:
- 如果有序的如下面的PriorityQueue或者SortedSet,执行下面对应的方法
- 如果是无序的,则比较器为null,调用initFromCollection方法
- PriorityQueue: 绑定原有的比较器,然后调用initFromPriorityQueue方法
- SortedSet:绑定原有的比较器,然后调用initElementsFromCollection方法
- Collection:再继续判断:
3、三个init相关的方法
上面根据指定集合类型的不同,调用了不同的初始化方法:
initElementsFromCollection:这个方法的作用就是将原有集合的数组复制过来,因此适用于SortedSet,因为SortedSet中的元素已经有序了
initFromCollection:这个方法适用于指定集合无序的情况,先调用上面的initElementsFromCollection复制数组,然后再调用heapify方法调整。
initFromPriorityQueue:确定指定集合是PriorityQueue,说明原数组已经有序,则直接指定Object数组与size到原PriorityQueue上。如果不是PriorityQueue,则调用下面的initFromCollection
与上面两个不同的是,这个方法并没有复制数组而只是单纯的指针指向。
二、关键方法
1、扩容
当容量不够是时候,Object数组就需要扩容
1 | private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; |
扩容的原则注释写的很明白,如果数组小,就扩大一倍,不然就扩大一半
2、add与offer方法
向队列中增加一个元素,可以调用add或者offer方法,其实在实现上都是一样的,都是调用了offer方法
1 | public boolean add(E e) { |
获取当前数组大小,如果不够了就扩容,然后再i位置放入元素,调用siftUp方法来维护堆的性质
1 | private void siftUp(int k, E x) { |
迭代的方式,不断上滤
3、poll与heapify方法
对offer对应的就是poll方法了
1 | public E poll() { |
逻辑也很简单,返回数组第一个元素,然后把最后一个元素放到第一个上,然后调用siftDown方法下滤
1 | private void siftDown(int k, E x) { |
前面还有一个heapify,可以发现这就是一个完整的筛选法建堆的过程
1 | private void heapify() { |
4、remove方法
删除是把最后一个元素放到指定位置,然后执行上滤或者下滤,两者只会执行其中的一个。
1 | public boolean remove(Object o) { |
5、其他方法
其他的方法比如clear、size、contains等实现都与ArrayList相类似就不一一介绍,PriorityQueue的核心就是其内部实现的堆,以及堆性质的维护,包括下滤与筛选法建堆,迭代法上滤,还有删除的时候选择上滤或者下滤中的一种。另外就是比较器,默认情况下是一个最小堆,如果需要实现最大堆或者持有其他类型的元素,则需要在构造函数中输入一个自定义的Comparator。