详解PriorityQueue,自带优先级的队列都用来干什么

一、概述

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 可以用于管理服务器队列,其中每个服务器都有不同的负载情况。请求可以根据服务器的负载情况优先分配给负载较低的服务器。

四、总结

点到为止,这玩意后面各个应用场景的实现操作非常之多;大家有兴趣可以学学。

另:文章如有错误和遗漏,欢迎指正和指出。

相关推荐
向前看-3 小时前
验证码机制
前端·后端
xlsw_3 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹4 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭4 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫4 小时前
泛型(2)
java
超爱吃士力架4 小时前
邀请逻辑
java·linux·后端
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石4 小时前
12/21java基础
java
李小白665 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp5 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea