12.Java SDK源码分析系列笔记-PriorityQueue
1. PriorityQueue是什么
是一个队列,只不过加上了优先级的概念,换句话说队列里的元素是根据某种规则排好序的
2. 使用
public class PriorityQueueTest
{
public static void main(String[] args)
{
List<Integer> list = Arrays.asList(20, 19, 18, 17, 16, 15, 14, 13, 12, 11);
PriorityQueue<Integer> queue = new PriorityQueue<>(list);
System.out.println("最开始的元素:" + queue);
System.out.println("============");
queue.offer(11);
System.out.println("添加了一个11:" + queue);
System.out.println("============");
List<Integer> sort = new ArrayList<>();
while (!queue.isEmpty())
{
Integer poll = queue.poll();
System.out.println("堆顶是:" + poll);
sort.add(poll);
System.out.println("剩余的元素:" + queue);
System.out.println("----------");
}
System.out.println("============");
System.out.println("堆排序的结果:" + sort);
}
}
- 输出
最开始的元素:[11, 12, 14, 13, 16, 15, 18, 20, 17, 19]
============
添加了一个11:[11, 11, 14, 13, 12, 15, 18, 20, 17, 19, 16]
============
堆顶是:11
剩余的元素:[11, 12, 14, 13, 16, 15, 18, 20, 17, 19]
----------
堆顶是:11
剩余的元素:[12, 13, 14, 17, 16, 15, 18, 20, 19]
----------
堆顶是:12
剩余的元素:[13, 16, 14, 17, 19, 15, 18, 20]
----------
堆顶是:13
剩余的元素:[14, 16, 15, 17, 19, 20, 18]
----------
堆顶是:14
剩余的元素:[15, 16, 18, 17, 19, 20]
----------
堆顶是:15
剩余的元素:[16, 17, 18, 20, 19]
----------
堆顶是:16
剩余的元素:[17, 19, 18, 20]
----------
堆顶是:17
剩余的元素:[18, 19, 20]
----------
堆顶是:18
剩余的元素:[19, 20]
----------
堆顶是:19
剩余的元素:[20]
----------
堆顶是:20
剩余的元素:[]
----------
============
堆排序的结果:[11, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Process finished with exit code 0
3. 源码分析
3.1. 属性
public class PriorityQueue<E> extends AbstractQueue<E>
implements java.io.Serializable {
private static final long serialVersionUID = -7720805057305804111L;
//默认的大小为11
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;
//无参构造
public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
}
3.2. 有参构造
//有参构造方法。我们主要分析这个
public PriorityQueue(Collection<? extends E> c) {
if (c instanceof SortedSet<?>) {
SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
this.comparator = (Comparator<? super E>) ss.comparator();
initElementsFromCollection(ss);
}
else if (c instanceof PriorityQueue<?>) {
PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
this.comparator = (Comparator<? super E>) pq.comparator();
initFromPriorityQueue(pq);
}
//传入的List走的这个else逻辑
else {
this.comparator = null;
initFromCollection(c);
}
- initFromCollection
private void initFromCollection(Collection<? extends E> c) {
initElementsFromCollection(c);
heapify();
}
可以看出主要有两个步骤,一个是建立初始化元素到数组中,另一个是维护堆的属性
3.2.1. 初始化元素到数组中
- initElementsFromCollection
private void initElementsFromCollection(Collection<? extends E> c) {
//先转成数组,如果数组不是Object[]类型的,那么需要转成Object[]数组
//我们的是Integer数组,所以会走这段逻辑copy成Object数组
Object[] a = c.toArray();
if (a.getClass() != Object[].class)
a = Arrays.copyOf(a, a.length, Object[].class);
int len = a.length;
//元素不能有为null的
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;
}
3.2.2. 维护堆的属性
- heapify
private void heapify() {
//i会初始化为最后一个有左孩子或者右孩子后者两者都有的 非叶子节点的下标
//调用下沉操作维护堆的属性
//一直到头节点为止(i--)
for (int i = (size >>> 1) - 1; i >= 0; i--)
//位置i的元素为queue[i],对其执行下沉操作
siftDown(i, (E) queue[i]);
}
- siftDown
//数组中位置k的元素为x,对其执行下沉操作,维护这棵子树的堆属性
private void siftDown(int k, E x) {
//如果设置了自定义的comparator,那么使用之
if (comparator != null)
siftDownUsingComparator(k, x);
//我们没设置,那么走这一段
else
siftDownComparable(k, x);
}
3.2.2.1. 下沉操作
private void siftDownComparable(int k, E x) {
//必须实现了Comparable接口
Comparable<? super E> key = (Comparable<? super E>)x;
//数组的一半大小,这个是下沉操作终止的条件
//即一直往下调整到叶子节点为止
int half = size >>> 1;
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];
//c为左右孩子较小的那个,跟父亲(我自己)比,如果父亲较小,没必要调整,直接break退出
if (key.compareTo((E) c) <= 0)
break;
//否则把较小的孩子覆盖到父亲的位置
queue[k] = c;
//把较小的孩子作为下一个父亲,继续执行下沉操作
k = child;
}
//把key(其实就是x)放在他该在的位置
queue[k] = key;
}
3.3. 插入
- offer
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;
}
- siftUp
//数组中位置k的元素为x,对其执行上浮操作,维护这棵子树的堆属性
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
//没有comparator走这段
else
siftUpComparable(k, x);
}
3.3.1. 上浮操作
private void siftUpComparable(int k, E x) {
//必须实现Comparable接口
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
//获取父亲
int parent = (k - 1) >>> 1;
Object e = queue[parent];
//如果我比父亲大,那么不需要调整,直接break退出
if (key.compareTo((E) e) >= 0)
break;
//把父亲覆盖到我的位置
queue[k] = e;
//把父亲作为下一个节点,继续执行上浮操作
k = parent;
}
//把key(其实就是x)放在他该在的位置
queue[k] = key;
}
3.4. 删除
- poll
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;
}
- siftDown
//数组中位置k的元素为x,对其执行下沉操作,维护这棵子树的堆属性
private void siftDown(int k, E x) {
//如果设置了自定义的comparator,那么使用之
if (comparator != null)
siftDownUsingComparator(k, x);
//我们没设置,那么走这一段
else
siftDownComparable(k, x);
}
3.4.1. 下沉操作
private void siftDownComparable(int k, E x) {
//必须实现了Comparable接口
Comparable<? super E> key = (Comparable<? super E>)x;
//数组的一半大小,这个是下沉操作终止的条件
//即一直往下调整到叶子节点为止
int half = size >>> 1;
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];
//c为左右孩子较小的那个,跟父亲(我自己)比,如果父亲较小,没必要调整,直接break退出
if (key.compareTo((E) c) <= 0)
break;
//否则把较小的孩子覆盖到父亲的位置
queue[k] = c;
//把较小的孩子作为下一个父亲,继续执行下沉操作
k = child;
}
//把key(其实就是x)放在他该在的位置
queue[k] = key;
}
4. 参考
12.Java SDK源码分析系列笔记-PriorityQueue的更多相关文章
- Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析
Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析 前言 上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析已经介绍到Ioc容器 ...
- Spring Ioc源码分析系列--容器实例化Bean的四种方法
Spring Ioc源码分析系列--实例化Bean的几种方法 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到bean真正通过那些方式实例化出来的时候,并没有继续分 ...
- Spring mvc源码分析系列--Servlet的前世今生
Spring mvc源码分析系列--Servlet的前世今生 概述 上一篇文章Spring mvc源码分析系列--前言挖了坑,但是由于最近需求繁忙,一直没有时间填坑.今天暂且来填一个小坑,这篇文章我们 ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- MyCat源码分析系列之——结果合并
更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...
- MyCat源码分析系列之——BufferPool与缓存机制
更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...
- MyCat源码分析系列之——前后端验证
更多MyCat源码分析,请戳MyCat源码分析系列 MyCat前端验证 MyCat的前端验证指的是应用连接MyCat时进行的用户验证过程,如使用MySQL客户端时,$ mysql -uroot -pr ...
- jquery2源码分析系列
学习jquery的源码对于提高前端的能力很有帮助,下面的系列是我在网上看到的对jquery2的源码的分析.等有时间了好好研究下.我们知道jquery2开始就不支持IE6-8了,从jquery2的源码中 ...
- [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat
概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
随机推荐
- Windows Terminal 添加 git-bash
配置文件中 profiles 节点补充配置 { "guid": "{b453ae62-4e3d-5e58-b989-0a998ec441b7}", " ...
- Spring AI 增加混元 embedding 向量功能
上次我们讨论了如何将自己的开源项目发布到 Maven 中央仓库,确保其能够方便地被其他开发者使用和集成.而我们的项目 spring-ai-hunyuan 已经具备了正常的聊天对话功能,包括文本聊天和图 ...
- Codeforces Round 970 (Div. 3)
A. Sakurako's Exam 分类讨论即可,当a为奇数,无法消去1,或者a==0且b为奇数时,无法消去2 #include <bits/stdc++.h> using namesp ...
- 使用Python可视化磁场
引言 随着科学技术的发展,物理学中的很多概念变得越来越复杂,但我们可以利用 Python 这一强大的工具,将一些抽象的物理现象变得更加直观易懂.今天,我们将以"磁场可视化"为主题, ...
- Mybatis-Plus中的@TableName 和 table-prefix
简介 本文介绍Mybatis-Plus中的@TableName 和 table-prefix的使用. 介绍 在 MyBatis-Plus 中,@TableName 注解和 table-prefix 配 ...
- @Accessors lombok注解用法
最近学习代码看到很多有趣的注解:慢慢整理下: @Accessors注解 @Accessors注解官方给出的解释是:面向getter和setter的更流畅的API.用于生成和查找getter和sette ...
- 智能简历解析器实战教程:基于Spacy+Flask构建自动化人才筛选系统
一.项目背景与技术选型 在人力资源领域,每天需要处理数百份简历的HR团队面临巨大挑战:人工筛选效率低下.关键信息遗漏风险高.跨文档对比分析困难.本教程将构建一个端到端的智能简历解析系统,通过NLP技术 ...
- 为什么 G1 垃圾收集器不维护年轻代到老年代的记忆集?
为什么 G1 垃圾收集器不维护年轻代到老年代的记忆集? 在 G1 垃圾收集器中,不维护年轻代到老年代的记忆集(Remembered Set, RSet)是因为其设计特点和优化策略使得这种记忆集的维护既 ...
- 强化学习框架:OpenRLHF源码解读,模型处理
强化学习框架:OpenRLHF源码解读,模型处理 本文主要介绍 强化学习框架:OpenRLHF源码解读,模型处理 models框架设计 了解一下 OpenRLHF的模型框架设计范式: From:htt ...
- 在Podman中配置Dify Sandbox服务与外部PostgreSQL服务的网络连接
在Podman中配置Dify Sandbox服务与外部PostgreSQL服务的网络连接 引言 在容器化环境中,确保不同服务之间的可靠通信是至关重要的.本文将指导你如何使用Podman来配置Dify ...