不少刷过题的小伙伴多少应该听过PriorityQueue(优先队列)这个数据结构,而Java中的PriorityQueue(优先队列)本质其实就是个堆,它的每个元素都根据比较器进行比较后赋予了一个优先级,优先级最高(默认是数值最小的,及俗称的“小根堆”)的元素位于队列的头部(出队列的话优先级高的会先出)。
案例演示
我们举个例子吧
我们需要一个主函数,代码如下:
public static void main(String[] args) {
PriorityQueue<Employee> queue = new PriorityQueue<Employee>();
queue.add(new Employee("Lisi", 18));
queue.add(new Employee("Zhangsan", 99));
queue.add(new Employee("Wangwu", 22));
while (!queue.isEmpty()) {
Employee poll = queue.poll();
System.out.println("name: " + poll.getName() + " age: " + poll.getAge());
}
}
自定义一个Employee类,代码如下:
class Employee implements Comparable<Employee> {
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public int compareTo(Employee o) {
return this.getAge() - o.getAge();
}
}
上述例子的输出结果如下:
在这个示例中,我们首先创建了一个PriorityQueue
,并添加了一些Employee
对象,然后通过循环不断将队列内的元素弹出,并输出弹出元素的name
和age
,由于在自定义类Employee
中,我们使用年龄作为排序的依据(即重写了compareTo()
方法)
由于比较的是int类型的数,所以我们可以查看他相关的实现类
从如下源码中不难看出,真正决定元素顺序的逻辑是(x < y) ? -1 : ((x == y) ? 0 : 1)
即如同函数上面介绍里说的
the value 0 if this Integer is equal to the argument Integer; a value less than 0 if this Integer is numerically less than the argument Integer; and a value greater than 0 if this Integer is numerically greater than the argument Integer (signed comparison).
简单翻译下就是:
- 返回0:第一个数 = 第二个数
- 返回负数:第一个数 < 第二个数
- 返回正数:第一个数 > 第二个数
值得注意的是:不能添加null
元素,否则会抛出NullPointerException
异常(如下图)
源码分析
创建
Java中的PriorityQueue的底层是一个Object数组,初始化时,如果执行的是无参构造,默认大小11
添加
在向PriorityQueue中添加元素时,如果队列是空,添加的元素自然就是队首,否则会调整元素的位置以满足堆的性质,如果发现Queue的容量大于或等于队列的长度,就会自动调用grow()
方法进行扩容
调用siftUp()
函数调整树结构
会根据用户是否传入比较器来进行分别处理,如下图红色框框为传入比较器的情况,黄色框框为未传入比较器的情况
扩容
每次扩容会根据旧容量进行判断,如果旧容量小于64,则新容量在原长度加上2(oldCapacity + 2
),如果旧容量大于64,则新容量变为原来的2倍(oldCapacity >> 1
)。
其中*MAX_ARRAY_SIZE
* = Integer.
MAX_VALUE
- 8
为什么是减8?这是一个避免过界的经验值
删除
删除操作按最小元素优先的规则,每次弹出最小元素,如果被删除的元素不是唯一的元素,则需要进行调整位置的操作,保持小根堆的性质。
总结
总结一下:PriorityQueue在物理结构上是一个数组,但逻辑结构上是堆,会以特定的优先级进行排序,优先级高的数据会优先被弹出,如果PriorityQueue满了,会自动进行扩容。
特点:
- 基于优先级堆实现,元素按照一定的优先级顺序排列
- 可以通过自然顺序(元素自身的比较规则)或自定义的 Comparator 来确定元素的优先级
- 无界队列,没有固定的容量限制,可以根据需要自动扩展
其实这些特点总结起来就是→可以自定义设置优先级/排序,基于他的这个特点,PriorityQueue有以下适用场景:
- 任务调度:可以根据任务的优先级来安排执行顺序(可自定义排序)
- 事件处理:按照事件的重要性或紧急程度进行处理
- 资源分配:根据资源请求的优先级进行分配(可自定义优先级)
- 数据排序和筛选:可以快速获取优先级最高的元素,适用于需要逐步处理具有优先级的数据的情况