Java集合类学习--PriorityQueue

深入理解PriorityQueue实现原理、及源码分析 PriorityQueue是使用Object[]数组实现的一个优先队列,是线程不安全的。它与FIFO的队列的区别在于,优先队列每次出队的元素都是优先级最高的元素。那么怎么确定哪一个元素的优先级最高呢?PriorityQueue使用堆这种数据结构,加上用户自定义的Comparator来确定每次出队的元素总是队列里面最小的,这里Comparator就相当于指定优先级。所以我们先来看看堆这种数据结构。 ... 阅读详情

概述

PriorityQueue 意为优先队列,表示队列中的元素是有优先级的,也就是说元素之间是可比较的。因此,插入队列的元素要实现 Comparable 接口或者 Comparator 接口。

PriorityQueue 的继承结构如下:
在这里插入图片描述
通过上图可看出,PriorityQueue 没有实现 BlockingQueue 接口,并非阻塞队列。它在逻辑上使用「堆」(即完全二叉树)结构实现,物理上基于「动态数组」存储。

源码分析

构造器
1、简单构造器

// 构造器 1:无参构造器(默认初试容量为 11)
public PriorityQueue() {
    this(DEFAULT_INITIAL_CAPACITY, null);
}

// 构造器 2:指定容量的构造器
public PriorityQueue(int initialCapacity) {
    this(initialCapacity, null);
}

// 构造器 3:指定比较器的构造器
public PriorityQueue(Comparator<? super E> comparator) {
    this(DEFAULT_INITIAL_CAPACITY, comparator);
}

// 构造器 4:指定初始容量和比较器的构造器
public PriorityQueue(int initialCapacity,
                     Comparator<? super E> comparator) {
    // Note: This restriction of at least one is not actually needed,
    // but continues for 1.5 compatibility
    if (initialCapacity < 1)
        throw new IllegalArgumentException();
    // 初始化内部数组和比较器
    this.queue = new Object[initialCapacity];
    this.comparator = comparator;
}

这几个构造器的作用就是初始化内部数组和比较器。

2、复杂构造器

// 构造器 5:用给定集合初始化 PriorityQueue 对象
public PriorityQueue(Collection<? extends E> c) {
    // 如果集合是 SortedSet 类型
    if (c instanceof SortedSet<?>) {
        SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
        this.comparator = (Comparator<? super E>) ss.comparator();
        initElementsFromCollection(ss);
    }
    // 如果集合是 PriorityQueue 类型
    else if (c instanceof PriorityQueue<?>) {
        PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
        this.comparator = (Comparator<? super E>) pq.comparator();
        initFromPriorityQueue(pq);
    }
    else {
        this.comparator = null;
        initFromCollection(c);
    }
}

initElementsFromCollection:

// 使用给定集合的元素初始化 PriorityQueue
private void initElementsFromCollection(Collection<? extends E> c) {
    // 把集合转为数组
    Object[] a = c.toArray();
    // If c.toArray incorrectly doesn't return Object[], copy it.
    if (a.getClass() != Object[].class)
        a = Arrays.copyOf(a, a.length, Object[].class);
    int len = a.length;
    // 确保集合中每个元素不能为空
    if (len == 1 || this.comparator != null)
        for (int i = 0; i < len; i++)
            if (a[i] == null)
                throw new NullPointerException();
    // 初始化 queue 数组和 size
    this.queue = a;
    this.size = a.length;
}

initFromPriorityQueue:

private void initFromPriorityQueue(PriorityQueue<? extends E> c) {
    if (c.getClass() == PriorityQueue.class) {
        // 若给定的是 PriorityQueue,则直接进行初始化
        this.queue = c.toArray();
        this.size = c.size();
    } else {
        initFromCollection(c);
    }
}

initFromCollection:

private void initFromCollection(Collection<? extends E> c) {
    // 将集合中的元素转为数组,并赋值给 queue(上面已分析)
    initElementsFromCollection(c);
    // 堆化
    heapify();
}

heapify: 堆化,即将数组元素转为堆的存储结构

private void heapify() {
    // 从数组的中间位置开始遍历即可
    for (int i = (size >>> 1) - 1; i >= 0; i--)
        siftDown(i, (E) queue[i]);
}

siftDown: 向下筛选?暂未找到恰当的译法,但这不是重点,该方法的作用就是使数组满足堆结构(其思想与冒泡排序有些类似)。如下:

private void siftDown(int k, E x) {
    // 根据 comparator 是否为空采用不同的方法
    if (comparator != null)
        siftDownUsingComparator(k, x);
    else
        siftDownComparable(k, x);
}

siftDownUsingComparator:

private void siftDownUsingComparator(int k, E x) {
    // 数组的中间位置
    int half = size >>> 1;
    while (k < half) {
        // 获取索引为 k 的节点的左子节点索引
        int child = (k << 1) + 1;
        // 获取 child 的值
        Object c = queue[child];
        // 获取索引为 k 的节点的右子节点索引
        int right = child + 1;
        // 左子节点的值大于右子节点,则二者换位置
        if (right < size &&
            comparator.compare((E) c, (E) queue[right]) > 0)
            // 取左右子节点中较小的一个
            c = queue[child = right];
        // 给定的元素 x 与较小的子节点的值比较
        if (comparator.compare(x, (E) c) <= 0)
            break;
        // 将该节点与子节点互换
        queue[k] = c;
        k = child;
    }
    queue[k] = x;
}

siftDownComparable 方法代码如下:

private void siftDownComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>)x;
    int half = size >>> 1;        // loop while a non-leaf
    while (k < half) {
        int child = (k << 1) + 1; // assume left child is least
        Object c = queue[child];
        int right = child + 1;
        if (right < size &&
            ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
            c = queue[child = right];
        if (key.compareTo((E) c) <= 0)
            break;
        queue[k] = c;
        k = child;
    }
    queue[k] = key;
}

此方法与 siftDownUsingComparator 方法实现逻辑完全一样,不同的的地方仅在于该方法是针对 Comparable 接口,而后者针对 Comparator 接口,不再赘述。

此外 PriorityQueue 还有两个构造器,但都是通过上面的方法实现的,如下:

// 构造器 6:用给定的 PriorityQueue 初始化一个 PriorityQueue
public PriorityQueue(PriorityQueue<? extends E> c) {
    this.comparator = (Comparator<? super E>) c.comparator();
    initFromPriorityQueue(c);
}

// 构造器 7:用给定的 SortedSet 初始化 PriorityQueue
public PriorityQueue(SortedSet<? extends E> c) {
    this.comparator = (Comparator<? super E>) c.comparator();
    initElementsFromCollection(c);
}

成员变量

// 数组的默认初始容量
private static final int DEFAULT_INITIAL_CAPACITY = 11;

// 内部数组,用于存储队列中的元素
transient Object[] queue; // non-private to simplify nested class access

// 队列中元素的个数
private int size = 0;

// 队列中元素的比较器
private final Comparator<? super E> comparator;

// 结构性修改次数
transient int modCount = 0; // non-private to simplify nested class access

常用方法
add(E), offer(E) 两个入队操作方法如下:

// 实际是调用 offer 方法实现的
public boolean add(E e) {
    return offer(e);
}

public boolean offer(E e) {
    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] = e;
    else
        // 向上筛选?
        siftUp(i, e);
    return true;
}

扩容操作:

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
    // 原先容量
    int oldCapacity = queue.length;
    // Double size if small; else grow by 50%
    // 原容量较小时,扩大为原先的两倍;否则扩大为原先的 1.5 倍
    int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                     (oldCapacity + 2) :
                                     (oldCapacity >> 1));
    // overflow-conscious code
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 创建一个新的数组
    queue = Arrays.copyOf(queue, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

出队操作:

public E poll() {
    // 队列为空时,返回 null
    if (size == 0)
        return null;
    int s = --size;
    modCount++;
    // 队列第一个元素
    E result = (E) queue[0];
    // 队列最后一个元素
    E x = (E) queue[s];
    // 把最后一个元素置空
    queue[s] = null;
    if (s != 0)
        // 下沉
        siftDown(0, x);
    return result;
}

总结

(1)PriorityQueue是一个小顶堆;

(2)PriorityQueue是非线程安全的;

(3)PriorityQueue不是有序的,只有堆顶存储着最小的元素;

(4)入队就是堆的插入元素的实现;

(5)出队就是堆的删除元素的实现;

基于STM32的宠物智能照护嵌入式系统设计 嵌入式系统是实现物联网终端设备本地化感知、决策与执行的核心技术载体,其关键在于多传感器融合、低功耗执行控制与轻量级云边协同。本文围绕环境监测(温湿度、烟雾、余粮)、机电执行(步进电机食盘、继电器风扇)和可靠通信(MQTT+ESP8266)三大技术主线,深入解析STM32F103在资源约束下的外设调度策略、ADC采样抗干扰设计、DHT11时序精准控制及HX711称重滤波算法等工程实践要点。系统强调本地闭环优先原则,确保离线基础功能可用,同时通过阿里云IoT平台实现远程监控与告警联动,为中小型智能硬件项目提供可 阅读详情

相关推荐

飞塔防火墙CVE-2022-40684漏洞复现:手把手教你搭建靶场环境(附调试技巧)

本文详细指导如何搭建Fortinet飞塔防火墙CVE-2022-40684认证绕过漏洞的靶场环境,涵盖虚拟机部署、网络配置、调试模式开启及漏洞原理分析。通过手把手教程,读者可掌握漏洞复现与深度调试技巧,深入理解该认证绕过漏洞的利用与防御。

h6i7j8的博客 695

优先队列源码分析及相关算法题

优先队列这种数据结构很有用,能够帮助我们解决很多算法题。

weixin_43691723的博客 465

【论文精读】Formal Mathematical Reasoning: A New Frontier in AI

注:本文根据师兄汇报内容总结。

m0_60634555的博客 1189

优先队列学习总结

队列: 现实中我们最常见的是,去医院挂号,去银行取钱,一般都需要排队,这就是队列,队列有一个最显著的特征: 先进先出,一般情况下,大多都是先到先办理,但是也是有特殊的情况,例如在医院,万一来个急诊的,一定是急诊的优先,或者在银行,来个VIP,有绿色通道。 这种特殊的情况下,就是队列中的特殊的队列:优先队列。它的实现方法:给入队的元素,赋予一个优先级,根据优先级,将其插入队列中,(...

Marmara 635

手把手教学,Python 游戏编程之实现飞机大战(含源代码)

我想大家都是有玩过类似飞机大战的射击类游戏,也享受目标被消除通过后带来的愉悦感。那么如果用Python来实现飞机大战游戏?下面就跟大家一起用Python来做一个小游戏,不仅可以从中感受到乐趣,还可以提升自己的能力。

veratata的博客 1万+

【Bug已解决】npm install -g @anthropic-ai/claude-code 出现 EACCES: permission denied 解决方案

方案适用场景推荐指数nvm 管理 Node长期开发环境,一次配置永久生效⭐⭐⭐⭐⭐修改 npm 全局目录不想引入 nvm,快速解决⭐⭐⭐⭐修复现有目录权限之前用过 sudo 装包,需要修复⭐⭐⭐Windows 管理员/改目录Windows 平台专用⭐⭐⭐⭐官方原生安装脚本想彻底避开 npm 权限问题⭐⭐⭐⭐报的本质是npm 全局安装目录的属主权限与当前执行用户不匹配,而不是 npm 或 Claude Code 本身有问题。不要用sudo"强行"解决。

一键难忘的博客 1万+

【Java 集合 & 数据结构】优先队列 PriorityQueue

优先队列 PriorityQueue 是 接口 Queue的实现,可以对其中元素进行排序,可以放基本的包装类型或自定义类型,对于包装类型默认为升序,对于自定义类型需手动实现 Comparator 接口。

茶色喵的博客 1953

【WorkBuddy专栏25】如何选择大模型——积分消耗、用途场景、选型决策全指南

【WorkBuddy专栏25】如何选择大模型——积分消耗、用途场景、选型决策全指南

AI人人懂 931

JDK源码那些事儿之PriorityBlockingQueue

今天继续说一说阻塞队列的实现,今天的主角就是优先级阻塞队列PriorityBlockingQueue,从命名上看觉得应该是有序的,毕竟是优先级队列,那么实际上是什么情况,我们一起看下其内部实现,提前说明下,因为PriorityBlockingQueue涉及到了堆排序的相关使用,如果没了解清楚,可以参考我之前写的关于堆排序的相关说明 前言 JDK版本号:1.8.0_171 Priori...

weixin_30763397的博客 184

高通开发系列 - System之FOTA/DFOTA包制作和升级

高通开发系列 - System之FOTA/DFOTA包制作和升级

fulinux的博客 4222

滑模控制算法(SMC)讲解-案例(附C代码)

该文章介绍了滑模控制算法的原理实现步骤,特点及关键问题,最后以车辆控制案例来讲解滑模控制的整个步骤实现

heda3的博客 4111

深入学习Java集合类源码之PriorityQueue

一.最小堆和最大堆 最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。 最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子 二.继承关系 public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable ...

atomatom98的博客 277

高精地图构建:Lidar SLAM与视觉SLAM对比

高精地图构建:Lidar SLAM与视觉SLAM对比​ ,人工智能,计算机视觉,大模型,AI,本文围绕高精地图构建,对比 Lidar SLAM 与视觉 SLAM。Lidar SLAM 通过激光获取点云数据,精度高、环境适应性强但成本高、数据量大;视觉 SLAM 利用图像,成本低、信息丰富却受光照影响大、深度获取难。还解析了 SLAM、点云等关键概念,介绍了两者融合趋势、应用案例、技术挑战及未来方向,也给出常见问题解答,为相关领域应用与学习提供参考。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 17万+

java集合类库学习记录———PriorityQueue

1.特点 优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator进行排序,具体取决于所使用的构造方法。 优先级队列不允许使用 null 元素。这是目前看到的第一个不允许放入null的集合。 优先级队列的内部结构是一个数组构成的最小堆(从0下标开始)。不了解堆数据结构的可以看数据结构与算法。 方法 iterator()中提供的迭代器不 保证以任何特定的顺序遍历

qwelkjzxc369的博客 328

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

JAVA - Collections用法总结

一生二,二生三,三生万物,基础永远是一个计算机人的立身之本。数据结构这门课程的分析奠定了工程师对各种平台中的容器类,集合类的理解基础,正如好多人所说的,如果你对某个平台的集合类理解的不透彻,很可能,你并不是不会使用那个平台上的代码,而是数据结构没理解透彻。 Windows NT平台上,MFC, ATL提供的集合类很少,而且功能很弱,这就导致了事实上的标准成了STL,相比于.Net 和 Jav...

weixin_33807284的博客 74

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Java数据结构原理与JDK源码对照:从ArrayList到红黑树

数据结构是Java工程师必备的基础知识,也是面试与工程排查的核心线索。从数组、链表到哈希表、红黑树,每种结构的存取方式与复杂度特征,决定了它在实际系统中的适用场景。理解原理后,再看JDK中ArrayList的1.5倍扩容机制、HashMap的哈希冲突与红黑树化、TreeMap的平衡策略、ArrayDeque的循环数组设计,就能明白官方实现背后的工程取舍。这些源码远非简单的API,而是经过海量场景验证的最佳实践。当接口变慢、内存飙高时,往往只需调整集合类型或初始化容量就能明显改善。本文以“原理对照源码”为线索

weixin_34199764的博客 154

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

【Java 集合类】PriorityQueue简单入门

合并K个有序链表,Java实现

破旧牛仔裤的基地 1210

Java 集合类入门篇

开门见山地说吧,Java 提供了一套完整的集合类(也可以叫做容器类)来管理一组长度可变的对象(也就是集合的元素),其中常见的类型包括 List、Set、Queue 和 Map。从我个人的编程经验来看,List 的实现类 ArrayList 和 Map 的实现类 HashMap 使用频率最高,其它实现类只能望其项背了。

沉默王二 4608

java集合类深入分析之PriorityQueue

PriorityQueue介绍     在平时的编程工作中似乎很少碰到PriorityQueue(优先队列) ,故很多人一开始看到优先队列的时候还会有点迷惑。优先队列本质上就是一个最小堆。前面一篇文章介绍了堆排序和堆的性质。而堆又是什么呢?它是一个数组,不过满足一个特殊的性质。我们以一种完全二叉树的视角去看这个数组,并用二叉树的上下级关系来映射到数组上面。如果是最大堆,则二叉树的顶点是保存的...

点缀星辰 755

Java 中有哪些常用的集合类?

除了上述的集合类,还有一些其他的集合类和接口可供使用,如 LinkedHashSet、LinkedHashMap、EnumSet、EnumMap 等,它们都有各自特定的用途和适用场景。这些集合类和接口提供了不同的功能和性能特点,您可以根据具体的需求选择适当的集合类来使用。在 Java 中,有许多常用的集合类可供使用。前面提到的 HashSet 和 HashMap。

weixin_44798281的博客 1705

java集合类

目录 1. Java集合类基本概念 2. Java集合类架构层次关系 3. Java集合类的应用场景代码 1. Java集合类基本概念 在编程中,常常需要集中存放多个数据。从传统意义上讲,数组是我们的一个很好的选择,前提是我们事先已经明确知道我们将要保存的对象的数量。一旦在数组初始化时指定了这个数组长度,这个数组长度就是不可变的,如果我们需要保存一个可以动态增长的数据(在编译时无法确...

uyhbrr的博客 281

JDK1.8 集合类 PriorityQueue 源码学习

概述 PriorityQueue 继承 AbstractQueue 抽象类,实现 Serializable接口 AbstractQueue 抽象类 PriorityQueue 继承 AbstractCollection 抽象类,实现 Queue接口 package java.util; /** * This class provides skeletal implementations of ...

随笔 249

java list 二次排序_Java集合类的那点通俗的认知

文/沉默王二开门见山地说吧,Java提供了一套完整的集合类(也可以叫做容器类)来管理一组长度可变的对象(也就是集合的元素),其中常见的类型包括List、Set、Queue和Map。从我个人的编程经验来看,List的实现类ArrayList和Map的实现类HashMap使用频率最高,其它实现类只能望其项背了。List、Set和Queue都是Collection的子接口,但各有各的好。List按照插入...

weixin_34635170的博客 577

PriorityQueue和PriorityBlockingQueue

文章目录简介PriorityQueuePriorityBlockingQueue PriorityQueue和PriorityBlockingQueue 简介 Queue一般来说都是FIFO的,当然之前我们也介绍过Deque可以做为栈来使用。今天我们介绍一种PriorityQueue,可以安装对象的自然顺序或者自定义顺序在Queue中进行排序。 PriorityQueue 先看PriorityQu...

程序那些事 1万+

常用的集合类

Java 集合框架(Java Collections Framework, JCF)是 Java 提供的一组接口和类,用于处理集合数据。集合框架的核心接口包括CollectionListSetQueue和Map,它们代表了不同类型的集合结构。通过这些接口,Java 提供了各种具体实现,能够满足多样化的数据处理需求。Java 提供了丰富多样的集合类,以满足不同场景下的数据管理需求。ArrayList和LinkedList是最常用的列表实现类,分别适用于频繁读取和频繁插入删除的场景。HashSet和。

Flying_Fish_roe的博客 1277

java 集合类 方法_Java 集合类入门篇

开门见山地说吧,Java 提供了一套完整的集合类(也可以叫做容器类)来管理一组长度可变的对象(也就是集合的元素),其中常见的类型包括 List、Set、Queue 和 Map。从我个人的编程经验来看,List 的实现类 ArrayList 和 Map 的实现类 HashMap 使用频率最高,其它实现类只能望其项背了。List、Set 和 Queue 都是 Collection 的子接口,但各有各的...

weixin_42502382的博客 178
上一篇: Java集合类学习--Map
下一篇: Java集合类学习--HashMap(一)
CodersCoder
博客等级 码龄10年 90粉丝 258原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值