一、PriorityQueue作用和源码

PriorityQueue跟前几个阻塞队列不一样,并没有实现BlockingQueue接口,只是实现了Queue接口,Queue接口中定义了几组放数据和取数据的方法,来满足不同的场景

二、PriorityQueue类结构

  public class PriorityQueue<E>
extends AbstractQueue<E>
implements java.io.Serializable { /**
* 数组初始容量大小
*/
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; }

可以看出PriorityQueue底层是基于数组实现的,使用Object[]数组存储元素,并且定义了比较器comparator,用于排序元素的优先级

三、初始化

PriorityQueue常用的初始化方法有4个:

1、无参构造方法

2、指定容量大小的有参构造方法

3、指定比较器的有参构造方法

4、同时指定容量和比较器的有参构造方法

/**
* 无参构造方法
*/
PriorityQueue<Integer> blockingQueue1 = new PriorityQueue<>(); /**
* 指定容量大小的构造方法
*/
PriorityQueue<Integer> blockingQueue2 = new PriorityQueue<>(10); /**
* 指定比较器的有参构造方法
*/
PriorityQueue<Integer> blockingQueue3 = new PriorityQueue<>(Integer::compareTo); /**
* 同时指定容量和比较器的有参构造方法
*/
PriorityQueue<Integer> blockingQueue4 = new PriorityQueue<>(10, Integer::compare);

再看一下对应的源码实现:

  /**
* 无参构造方法
*/
public PriorityQueue() {
// 使用默认容量大小11,不指定比较器
this(DEFAULT_INITIAL_CAPACITY, null);
} /**
* 指定容量大小的构造方法
*/
public PriorityQueue(int initialCapacity) {
this(initialCapacity, null);
} /**
* 指定比较器的有参构造方法
*/
public PriorityQueue(Comparator<? super E> comparator) {
this(DEFAULT_INITIAL_CAPACITY, comparator);
} /**
* 同时指定容量和比较器的有参构造方法
*/
public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) {
if (initialCapacity < 1) {
throw new IllegalArgumentException();
}
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}

可以看出PriorityQueue的无参构造方法使用默认的容量大小11,直接初始化数组,并且没有指定比较器

三、offer方法源码

先看一下offer()方法源码,其他放数据方法逻辑也是大同小异,都是在链表尾部插入。 offer()方法在队列满的时候,会直接返回false,表示插入失败

  public boolean offer(E e) {
// 1. 判空,传参不允许为null
if (e == null) {
throw new NullPointerException();
}
modCount++;
int i = size;
// 2. 当数组满的时候,执行扩容
if (i >= queue.length) {
grow(i + 1);
}
size = i + 1;
// 3. 如果是第一次插入,就直接把元素插入到数组头部
if (i == 0) {
queue[0] = e;
} else {
// 4. 如果不是第一次插入,就找个合适的位置插入(需要保证插入后数组有序)
siftUp(i, e);
}
return true;
}

offer()方法逻辑也很简单,先判断是否需要扩容,如果需要扩容先执行扩容逻辑,然后把元素插入到数组中。如果是第一次插入,就直接把元素插入到数组头部。如果不是,就找个合适的位置插入,需要保证插入后数组仍是有序的。 再看一下扩容的源码:

/**
* 扩容
*/
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// 1. 如果原数组容量小于64,就执行2倍扩容,否则执行1.5扩容
int newCapacity = oldCapacity +
((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1));
// 2. 校验最大容量不能超过Integer最大值
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = hugeCapacity(minCapacity);
}
// 3. 直接扩容后新数组赋值给原数组
queue = Arrays.copyOf(queue, newCapacity);
}

扩容的源码设计充满了作者的巧思,在数组容量较小的时候,为了避免频繁扩容,就采用2倍扩容法。在数组容量较大的时候,为了避免扩容后浪费空间,就采用1.5倍扩容法。

PriorityQueue为了快速的插入和删除,采用了最小堆,而不是直接使用有序数组,这样既可以保证插入和删除的时间复杂度都是O(logn),又能避免移动过多元素。 最小堆的定义:除叶子节点外,每个节点的值都小于等于左右子节点的值。

下面就是一个简单的最小堆和映射数组:

再看一下siftUp()方法源码,是怎么保证插入元素,数组仍是有序的? 其实就是循环跟父节点比较元素大小,找个合适的位置插入。

  // 把元素插入到合适的位置
private void siftUp(int k, E x) {
// 1. 如果初始化的时候,自定义了比较器,就使用自定义比较器的插入方法,否则使用默认的。
if (comparator != null) {
siftUpUsingComparator(k, x);
} else {
siftUpComparable(k, x);
}
} // 自定义比较器的插入方法
private void siftUpUsingComparator(int k, E x) {
while (k > 0) {
// 1. 找到父节点
int parent = (k - 1) >>> 1;
Object e = queue[parent];
// 2. 如果当前节点元素比父节点的元素小,就把父节点元素向下移动(给当前元素腾出位置)
if (comparator.compare(x, (E) e) >= 0) {
break;
}
queue[k] = e;
k = parent;
}
// 3. 把当前元素插入到父节点的位置
queue[k] = x;
} // 默认的插入方法
private void siftUpComparable(int k, E x) {
// 1. 使用默认比较器
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
// 2. 找到父节点
int parent = (k - 1) >>> 1;
Object e = queue[parent];
// 3. 如果当前节点元素比父节点的元素小,就把父节点元素向下移动(给当前元素腾出位置)
if (key.compareTo((E) e) >= 0) {
break;
}
queue[k] = e;
k = parent;
}
// 4. 把当前元素插入到父节点的位置
queue[k] = key;
}

PriorityQueue作用和源码的更多相关文章

  1. jQuery静态方法globalEval使用和源码分析

    Eval函数大家都很熟悉,但是globalEval方法却很少使用,大多数参考手册也没有相关api,下面就对其用法和源码相应介绍: jQuery.globalEval()函数用于全局性地执行一段Java ...

  2. Quartz学习--二 Hello Quartz! 和源码分析

    Quartz学习--二  Hello Quartz! 和源码分析 三.  Hello Quartz! 我会跟着 第一章 6.2 的图来 进行同步代码编写 简单入门示例: 创建一个新的java普通工程 ...

  3. Dubbo原理和源码解析之服务引用

    一.框架设计 在官方<Dubbo 开发指南>框架设计部分,给出了引用服务时序图: 另外,在官方<Dubbo 用户指南>集群容错部分,给出了服务引用的各功能组件关系图: 本文将根 ...

  4. react-router@4.0 使用和源码解析

    如果你已经是一个正在开发中的react应用,想要引入更好的管理路由功能.那么,react-router是你最好的选择~react-router版本现今已经到4.0.0了,而上一个稳定版本还是2.8.1 ...

  5. 源码详解系列(八) ------ 全面讲解HikariCP的使用和源码

    简介 HikariCP 是用于创建和管理连接,利用"池"的方式复用连接减少资源开销,和其他数据源一样,也具有连接数控制.连接可靠性测试.连接泄露控制.缓存语句等功能,另外,和 dr ...

  6. 15分钟带你了解前端工程师必知的javascript设计模式(附详细思维导图和源码)

    15分钟带你了解前端工程师必知的javascript设计模式(附详细思维导图和源码) 前言 设计模式是一个程序员进阶高级的必备技巧,也是评判一个工程师工作经验和能力的试金石.设计模式是程序员多年工作经 ...

  7. [源码分析] 从实例和源码入手看 Flink 之广播 Broadcast

    [源码分析] 从实例和源码入手看 Flink 之广播 Broadcast 0x00 摘要 本文将通过源码分析和实例讲解,带领大家熟悉Flink的广播变量机制. 0x01 业务需求 1. 场景需求 对黑 ...

  8. 一文搞懂 CountDownLatch 用法和源码!

    CountDownLatch 是多线程控制的一种工具,它被称为 门阀. 计数器或者 闭锁.这个工具经常用来用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用).下面我们就来一起 ...

  9. Kubernetes Job Controller 原理和源码分析(二)

    概述程序入口Job controller 的创建Controller 对象NewController()podControlEventHandlerJob AddFunc DeleteFuncJob ...

  10. Spark详解(08) - Spark(3.0)内核解析和源码欣赏

    Spark详解(08) - Spark(3.0)内核解析和源码欣赏 源码全流程 Spark提交流程(YarnCluster) Spark通讯架构 Spark任务划分 Task任务调度 Shuffle原 ...

随机推荐

  1. superset 图表加水印

    转载wenqiang1208的文章 superset 作为一个数据可视化的工具,其中的图表,报表数据是非常敏感的,为了防止数据外泄,大部分公司需要在敏感图表上加上水印. 本篇文章有2种方式去介绍如何在 ...

  2. 上云有道 | 一图读懂天翼云边缘安全加速平台AccessOne!

    上云有道 | 一图读懂天翼云边缘安全加速平台AccessOne!

  3. Luogu P1983 车站分级 题解 [ 绿 ] [ 拓扑排序 ] [ 图论建模 ] [ 虚点 ]

    车站分级:很好的拓扑排序题,细节有点多. 图论建模 首先观察对于一条线路,我们可以从中直接得到什么信息: 假设这条线路的开头为 \(st\),结尾为 \(ed\),那么在 \([st,ed]\) 的车 ...

  4. 腾讯元宝接入 DeepSeek R1 模型,支持深度思考 + 联网搜索,好用不卡机!

    前言 腾讯元宝AI产品于2025年2月13日在应用商店发布更新,正式接入了DeepSeek R1模型,并宣布该模型已联网.满血上线,DeepSeek+腾讯混元,好用不卡机. 腾讯元宝介绍 腾讯元宝是依 ...

  5. WPF 线程处理

    参考链接:https://docs.microsoft.com/zh-cn/previous-versions/dotnet/netframework-3.5/ms771750(v=vs.90)

  6. 交叉编译SQLite3

    交叉编译SQLite3 SQLite是一个进程内的库,实现了自给自足的.无服务器的.零配置的.事务性的SQL 数据库引擎. 它是一个零配置的数据库,这意味着与其他数据库不一样,您不需要在系统中配置. ...

  7. sql server 新建用户数据库授权

    必须对数据库进行 db_owner 授权.

  8. Qt QFileSystemModel 的使用

    Model 指的是数据 View 指的是界面,View不用设置,只需要和Model进行绑定,绑定完成之后就是Model的格式了 例子:本例子中QListView QTableView QTreeVie ...

  9. Linux服务器快速卸载安装node环境(简单上手)

    这篇文章主要介绍了Linux服务器快速卸载安装node环境(简单上手) 1.先卸载npm sudo npm uninstall npm -g 2.卸载node yum remove nodejs np ...

  10. 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性

    学习编程之初就常被告诫:"永远不要相信用户的输入",但实际编码中,可能因为各种原因而忽略这点,本文尝试以 SQL 注入的角度探寻校验输入的重要性 以下实验均以 SQLI labs ...