一、概述
PriorityQueue
是Java中的一个实现了Queue
接口的类,它提供了优先级队列的功能。优先级队列根据元素的优先级进行排序,确保每次操作都能够返回优先级最高的元素。
PriorityQueue
不是线程安全的,如果在多线程环境中使用,需要进行适当的同步处理。
1.1 PriorityQueue 的特点
元素排序 :默认情况下,
PriorityQueue
使用元素的自然顺序来确定优先级。也可以通过自定义比较器(Comparator
)来指定元素的优先级规则。堆实现 :
PriorityQueue
内部使用堆(heap
)数据结构来维护元素的顺序。具体来说,它使用二叉堆(binary heap
)实现,具有较好的插入、删除和获取最高优先级元素的性能。动态大小 :
PriorityQueue
具有动态大小,可以根据需要自动调整容量。它可以根据插入的元素数量自动扩展容量,并在需要时收缩容量,以提供更好的内存使用效率。不允许存储
null
元素 :PriorityQueue
不允许存储null
元素,否则会抛出NullPointerException
。
1.2 PriorityQueue 的常用方法
PriorityQueue
本身提供了非常多的方法,详情如下图:
常用的操作包括:
offer(E e)
:将指定元素插入队列。
poll()
:删除并返回队列中的最高优先级元素。
peek()
:返回队列中的最高优先级元素,但不删除它。
size()
:返回队列中的元素数量。
isEmpty()
:检查队列是否为空。
除了上面的常见操作之外,还有一些特别操作:
remove(Object o)
: 该方法用于从队列中移除指定的元素。如果队列中存在多个相等的元素,它将删除其中一个。如果元素不存在于队列中,不会引发异常,而是返回false
。
addAll(Collection<? extends E> c)
: 该方法用于将指定集合中的所有元素添加到队列中。添加的顺序不受元素的优先级影响,而是按照集合的迭代顺序添加。
iterator()
: 该方法返回一个迭代器,用于遍历队列中的元素。迭代器遵循队列的有序性,从最高优先级的元素开始,依次返回元素。迭代器不会修改队列的结构,但如果在迭代过程中修改了队列,迭代器的行为将变得不确定。
toArray()
和toArray(T[] a)
: toArray()方法返回一个包含队列中所有元素的数组,按照元素的优先级顺序排列。toArray(T[] a)方法也返回一个包含队列中所有元素的数组,但可以指定返回的数组类型。如果指定的数组大小不足以容纳队列中的所有元素,将创建一个新的数组来存储元素。
二、PriorityQueue 是否支持自定义比较器呢?
可以肯定,PriorityQueue
支持通过自定义比较器(Comparator
)来指定元素的优先级规则。
PriorityQueue
的构造函数可以接受一个 Comparator
对象作为参数,用于定义元素的优先级比较规则。比较器可以根据元素的特定属性、自定义逻辑或其他需要来确定元素的优先级顺序。
2.1 自定义实现的案例
下面我将使用一个案例来实现自定义比较器:
需求描述:
1、期末考试完毕了,班主任需要来录入学生的成绩。
2、学生录入的信息包括,姓名,性别,成绩。
3、假设录入的成绩是存在内存里面的,然后我们需要给录入的成绩排序。
现在我们来看看如何实现这个需求:
- 首先定义一个学生类:
学生类是系统的核心数据结构,包含学生的姓名、性别和成绩等信息。
java
public class Student {
/**
* 姓名
*/
private String name;
/**
* 性别
*/
private String gender;
/**
* 成绩
*/
private Double score;
public Student(String name, String gender, Double score) {
this.name = name;
this.gender = gender;
this.score = score;
}
public String getName() {
return name;
}
public String getGender() {
return gender;
}
public Double getScore() {
return score;
}
}
- 其次班主任来录入成绩:
java
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
// 创建学生队列,并使用自定义比较器按照成绩降序排列
Queue<Student> studentQueue = new PriorityQueue<>(Comparator.comparingDouble(Student::getScore).reversed());
// 录入学生成绩
studentQueue.offer(new Student("张三", "男", 85.0));
studentQueue.offer(new Student("李四", "女", 78.0));
studentQueue.offer(new Student("王五", "男", 92.0));
studentQueue.offer(new Student("赵六", "女", 80.0));
// 按照成绩排队输出
while (!studentQueue.isEmpty()) {
Student student = studentQueue.poll();
System.out.println("姓名: " + student.getName() +
", 性别: " + student.getGender() +
", 成绩: " + student.getScore());
}
}
}
注意:上面代码里面就实现了按照成绩降序排列来自定义比较器
Queue<Student> studentQueue = new PriorityQueue<>(Comparator.comparingDouble(Student::getScore).reversed());
- 最后看看成绩排名信息:
java
姓名: 王五, 性别: 男, 成绩: 92.0
姓名: 张三, 性别: 男, 成绩: 85.0
姓名: 赵六, 性别: 女, 成绩: 80.0
姓名: 李四, 性别: 女, 成绩: 78.0
特别注意:
在上述示例中,如果两个学生的成绩相同,它们的排队顺序将取决于它们添加到队列中的顺序。先添加到队列的学生将先被排在前面,后添加的学生将被排在后面。
这是因为
PriorityQueue
在维护元素的优先级顺序时,如果两个元素的优先级相同,它们将根据它们在队列中的相对顺序进行排列。插入顺序越早的元素会被排在前面,插入顺序越晚的元素会被排在后面。这样可以保持元素的先进先出(FIFO
)顺序。
三、PriorityQueue 的应用场景
PriorityQueue
在许多其他领域也有广泛的应用,特别是需要按照优先级处理元素的情况。以下是一些常见的应用场景:
任务调度 :
PriorityQueue
可以用于实现任务调度器,其中每个任务都有不同的优先级。调度器可以按照任务的优先级顺序选择和执行任务。事件处理 :当系统需要按照事件的优先级处理时,
PriorityQueue
可用于管理事件队列。较高优先级的事件可以首先被处理,以确保及时响应重要事件。搜索算法 :在一些搜索算法中,如
Dijkstra
算法、A*
算法、Best-First
搜索和优先级搜索,需要根据节点的优先级来选择下一个要探索的节点。PriorityQueue
提供了高效的优先级队列实现,可以用于这些搜索算法的实现。任务优先级队列 :在多线程编程中,可以使用
PriorityQueue
来实现任务队列,其中每个任务都有不同的优先级。线程可以从队列中获取最高优先级的任务进行处理。事件调度 :在事件驱动的系统中,
PriorityQueue
可以用于管理待处理的事件队列。根据事件的优先级顺序,系统可以按照预定的顺序处理事件。负载均衡 :在负载均衡算法中,
PriorityQueue
可以用于管理服务器队列,其中每个服务器都有不同的负载情况。请求可以根据服务器的负载情况优先分配给负载较低的服务器。
四、总结
点到为止,这玩意后面各个应用场景的实现操作非常之多;大家有兴趣可以学学。
另:文章如有错误和遗漏,欢迎指正和指出。