Java集合框架(PriorityQueue优先级队列讲解)

简介: Java集合框架(PriorityQueue优先级队列讲解)

前言:

Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,本文主要介绍PriorityQueue

 

 priorityQueue在Java集合框架中的关系如下:


6136729e88fe440ba2881e9b068a47ad.png

一、使用PriorityQueue的注意点

 

1. 使用时必须导入PriorityQueue所在的包,即:

import java.util.PriorityQueue


2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出ClassCastException异常

3. 不能插入null对象,否则会抛出NullPointerException


4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容


5. 插入和删除元素的时间复杂度为log以2为底的n


6. PriorityQueue底层使用了堆数据结构


7. PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素(想要变成大堆,需要我们重新比较方法、默认的比较方法是Comparable接口中的compareTo方法


二、PriorityQueue常用接口介绍

1. 优先级队列的构造

🔔此处只是列出了PriorityQueue中常见的几种构造方式,其他的大家可以参考帮助文档。

image.png

2、PriorityQueue中对元素的比较

看完了构造方法,我们重新来思考一个问题

优先级队列是如何实现有序的呢?因为优先级队列就是借助小根堆来实现的

在实现小根堆的过程我们知道这其中一定发生了元素的比较,所以PriorityQueue中的元素必须要能够比较大小,那么PriorityQueue是如何进行元素的比较呢?


PriorityQueue采用了:

Comparble和Comparator两种方式。

1. Comparble是默认的内部比较方式,如果用户插入自定义类型对象时,该类对象必须要实现Comparble接口,并覆写compareTo方法


2. 用户也可以选择使用比较器对象,如果用户插入自定义类型对象时,必须要提供一个比较器类,让该类实现Comparator接口并覆写compare方法.

62cf644e28834ca1946d46b85093ca3b.png


我们看到这里程序报错了,为什么呢?因为我们插入的Child对象是不可比较的(即没有实现Comparable接口又没有采用自定义的比较器)

可以看到

514b7ee1b7b94b95bc71024d827c16f0.png

即我们采用了默认的比较方法Comparable接口中的compareTo方法

12359ab31da6414890718ec8e4635328.png

此时我们再次看一下报错信息

9b358385506c4251973f1556f0e39ba8.png

🔔看来是在往PriorityQueue中插入元素的时候出现了问题

 

😎那么我们打开PriorityQueue的源码看一下(下面是PriorityQueue中offer方法的源码)

a85492ac28b4486da6b857aaf4ffea11.png

再看看shiftUp的源码

3876af263f874913992e68769f1967f6.png

继续点进去siftUpComparable

4bd2300184834f98ac05c40911221282.png

此时再回过头来看看我们放到PriorityQueue中的Child对象,他即没有自定义比较器又没有实现Comparable接口,当然报错呀!

那么好吧我们在Child类中实现Comparable接口,按年龄进行比较

import java.util.PriorityQueue;
class Child implements Comparable<Child>{
    int age;
    String name;
    public Child(int age, String name) {
        this.age = age;
        this.name = name;
    }
    @Override
    public int compareTo(Child o) {
        return this.age - o.age;  // 默认实现小根堆
    }
    @Override
    public String toString() {
        return "Child{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
public class TestPriorityQueue {
    public static void main(String[] args) {
        PriorityQueue<Child> priorityQueue = new PriorityQueue<>();
        priorityQueue.offer(new Child(12, "小亮"));
        priorityQueue.offer(new Child(11, "小红"));
        priorityQueue.offer(new Child(8, "小强"));
        System.out.println(priorityQueue);
    }
}

5fcc6327830540928f0e8730e2f90a1d.png

如果要实现大根堆,也好办😁

2e75f826db7146559a3f707e14f18c05.png

上面我们是实现了Compable接口,那么如果我们自定义一个年龄比较器呢?

class Child {
    int age;
    String name;
    public Child(int age, String name) {
        this.age = age;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Child{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
class AgeComparator implements Comparator<Child> {
    @Override
    public int compare(Child o1, Child o2) {
        return o1.age - o2.age; // 实现小根堆
        // return o2.ae - o1.age 实现大根堆
    }
}
public class TestPriorityQueue {
    public static void main(String[] args) {
        AgeComparator ageComparator = new AgeComparator();
        // 创建具有默认初始容量的 PriorityQueue ,并根据指定的比较器对其元素进行排序。
        PriorityQueue<Child> priorityQueue = new PriorityQueue<>(ageComparator);
        // 可以看到这里我的Child对象虽然没有实现Comparable接口,但因为我们对Child对象自定义了一个年龄比较器,所以插入元素不会报错
        priorityQueue.offer(new Child(12, "小亮"));
        priorityQueue.offer(new Child(11, "小红"));
        priorityQueue.offer(new Child(8, "小强"));
        System.out.println(priorityQueue);
    }
}

6b861a61a54448e7b89ac8ea98a0814f.png

3、插入/删除/获取优先级最高的元素

image.png


相关文章
|
3天前
|
设计模式 算法 Java
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
|
3天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
3天前
|
Dubbo Java 应用服务中间件
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
|
3天前
|
存储 Java C++
Java集合篇之深度解析Queue,单端队列、双端队列、优先级队列、阻塞队列
Java集合篇之深度解析Queue,单端队列、双端队列、优先级队列、阻塞队列
17 0
|
8天前
|
Java Maven 开发工具
《Java 简易速速上手小册》第5章:Java 开发工具和框架(2024 最新版)
《Java 简易速速上手小册》第5章:Java 开发工具和框架(2024 最新版)
28 1
|
12天前
|
Java 大数据 云计算
Spring框架:Java后台开发的核心
【4月更文挑战第15天】Spring框架在Java后台开发中占据核心位置,因其控制反转(IoC)、面向切面编程(AOP)、事务管理等特性提升效率和质量。Spring提供数据访问集成、RESTful Web服务和WebSocket支持。优势包括高效开发、灵活扩展、强大生态圈和广泛应用。应用于企业级应用、微服务架构及云计算大数据场景。掌握Spring对Java开发者至关重要。
|
15天前
|
存储 Java 编译器
Java集合丛林:深入了解集合框架的秘密
Java集合丛林:深入了解集合框架的秘密
16 0
Java集合丛林:深入了解集合框架的秘密
|
18天前
|
Java BI
Java 获取周,月,年日期集合(统计图)
Java 获取周,月,年日期集合(统计图)
Java 获取周,月,年日期集合(统计图)
|
19天前
|
存储 Java 数据库连接
java使用mp持久化框架,写入5000个字符,但是VARCHAR(255) 会报错
使用Java的MyBatis Plus框架时,如果尝试将超过VARCHAR(255)限制的字符串(如5000个字符)存入数据库,会抛出异常。解决方法是将列类型改为TEXT。可通过在实体类属性上添加`@TableField(typeHandler = JdbcType.CLOB)`注解,如`private String content;`,将属性映射到CLOB类型列,以存储更长字符串。
9 0
|
19天前
|
存储 Java
java反射——设计框架的灵魂
java反射——设计框架的灵魂