集合系列(十二) -PriorityQueue详解

本文详细介绍了Java中的PriorityQueue,一个基于优先级的无界队列,包括其底层数据结构(小顶堆),添加(offer)、删除(poll)和查询(peek)方法的实现,以及自然排序和自定义排序的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、摘要

在前几篇文章中,咱们了解到,Queue 的实现类有 ArrayDeque、LinkedList、PriorityQueue。

在上一章节中,陆续的介绍到 ArrayDeque 和 LinkedList 的数据结构和算法实现,今天咱们来介绍一下PriorityQueue 这个类,一个特殊的优先级队列。如果有理解不当之处,欢迎指正。

二、简介

PriorityQueue 并没有直接实现 Queue接口,而是通过继承 AbstractQueue 类来实现 Queue 接口一些方法,在 Java 定义中,PriorityQueue 是一个基于优先级的无界优先队列。

通俗的说,添加到 PriorityQueue 队列里面的元素都经过了排序处理,默认按照自然顺序,也可以通过 Comparator 接口进行自定义排序。

优先队列的作用是保证每次取出的元素都是队列中权值最小的。

如果猿友们了解过 TreeMap 的实现,会发现 PriorityQueue 排序实现与之类似。

PriorityQueue 是采用树形结构来描述元素的存储,具体说是通过完全二叉树实现一个小顶堆,在物理存储方面,PriorityQueue 底层通过数组来实现元素的存储。

在上图中,我们给每个元素的下标做了标注,足够细心的你会发现,数组下标,存在以下关系:

leftNo = parentNo * 2 + 1
rightNo = parentNo * 2 + 2
parentNo = (currentNo -1) / 2

各个参数具体含义如下:

  • parentNo:表示父节点下标;
  • leftNo:表示子元素左节点下标;
  • rightNo:表示子元素右节点下标;
  • currentNo:表示当前元素节点下标;

通过上述三个公式,可以轻易计算出某个节点的父节点以及子节点的下标。这也就是为什么可以直接用数组来存储元素实现二叉树结构的原因。

2.1、源码介绍

PriorityQueue 源码定义如下:

public class PriorityQueue<E> extends AbstractQueue<E>
    implements java.io.Serializable {
   
   
    
    /**默认容量为11*/
    private static final int DEFAULT_INITIAL_CAPACITY = 11;

    /**队列容器*/
    transient Object[] queue;

    /**队列长度*/
    private int size = 0;

    /**比较器,为null使用自然排序*/
    private final Comparator<? super E> comparator;

    ......
}

从定义中可以得出,PriorityQueue 有3个比较核心的变量属性,内容如下:

  • queue:表示存放元素的数组
  • comparator:表示比较器对象,如果为空,使用自然排序
  • size:表示队列长度

我们再来看看 PriorityQueue 类的构造方法,PriorityQueue 构造方法分两类,一种是默认初始化、另一种是传入 Comparator 接口比较器,内容如下:

默认初始化,使用自然排序方式进行插入,源码如下:

public PriorityQueue() {
   
   
    //默认数组长度为11,传入比较器为null
    this(DEFAULT_INITIAL_CAPACITY, null);
}

调用的方法,源码如下:

public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) {
   
   
    //初始化容量小于 1,抛异常
    if (initialCapacity < 1)
        throw new IllegalArgumentException();
    this.queue = new Object[initialCapacity];
    this.comparator = comparator;
}

自定义比较器初始化,使用 comparator 接口比较器作为参数传入,源码如下:

public PriorityQueue(Comparator<? super E> comparator) {
   
   
    //传入比较器 comparator
    this(DEFAULT_INITIAL_CAPACITY, comparator);
}

这两者初始化方式,咱们在下文会一一讲到。

在介绍 PriorityQueue 实现的方法之前,咱们了解到,Queue 接口定义有如下方法:

同样的 PriorityQueue 也实现了这些方法,PriorityQueue 方法虽然定义的很多,但无非就是对容器进行添加、删除、查询操作,下面我们分别来看看各个操作方法的实现过程。

三、常见方法介绍

3.1、添加方法

PriorityQueue 的添加方法有 2 种,分别是add(E e)offer(E e),两者语义相同,都是向优先队列中插入元素,只是Queue接口规定二者对插入失败时的处理不同,前者在插入失败时抛出异常,后则返回false

3.1.1、offer 方法

offer 方法图解实现流程如下:

新加入的元素可能会破坏小顶堆的性质,在 c、d 两步会进行调整。

offer 方法的实现,源码如下:

public boolean offer(E e) {
   
   
    //不允许放入null元素
    if (e == null)
        throw new NullPointerException();
    modCount++;
    int i = size;
    if (i >= queue.length)
        //自动扩容
        grow(i + 1);
    size = i + 1;
    //队列原来为空,这是插入的第一个元素
    if (i == 0)
        queue[0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值