CloudNativeEra
  • Introduction
  • 名词解释
  • Computer Science
    • Computer Organization
      • CPU
      • 二进制、电路、加法器、乘法器
      • 编译、链接、装载
      • 存储器
      • IO
    • Operating System
      • 操作系统基础知识
      • 系统初始化
      • 进程管理
      • Everything about Memory
      • 文件系统
      • 并行编程
      • Linux
        • CPU
        • IO 多路复用
        • DMA IO and Linux Zero Copy
    • Computer Network
      • 网络相关命令
      • 评估系统的网络性能
      • 网络抓包
      • Linux 最多支撑的 TCP 连接
      • 网络虚拟化
      • DHCP 工作原理
    • Data Structure and Algorithm
      • 题目列表
      • Summarize
        • 方法总结
        • 二分思想
        • 树的演化
        • 算法思想总结
      • Data Structure
        • Data Struct - Array
        • Tree
        • Heap
        • Hash
        • 字符串
      • Algorithm
        • Sorting Algorithm
        • 查找
        • 贪心算法
        • 动态规划
        • 位运算
      • Practice Topics
        • Data Struct in SDK
        • Topic - Tree
        • Topic - Graph
        • Topic - 滑动窗口
        • 剑指 Offer 题解
    • 并发编程
      • 并发模式
      • 并发模型
  • 系统设计
    • 软件设计
      • 软件架构
      • 编程范式
      • 系统设计题
      • 设计原则
      • 计算机程序的构造和解释 SICP
    • 领域驱动设计
      • 应用:在线请假考勤管理
      • 应用: library
    • 微服务与云原生
      • Designing and deploying microservices
      • 容器技术
      • Docker
      • Etcd
      • Kubernetes
        • Kubernetes - Mapping External Services
      • Istio
      • 监控
    • 分布式系统
      • 分布式理论
      • 分布式事务
    • 后端存储设计
      • 缓存设计
      • 数据库架构设计
    • CI/CD
    • 设计最佳实践
    • 测试
    • 安全
    • 综合
      • 开发实践
      • 分布式锁
      • 分布式计数服务
      • 弹幕系统设计
      • 消息队列设计
      • 分布式ID生成算法
      • 限流设计
      • 网关设计
      • 通用的幂等设计
      • 分布式任务调度
        • Timer
        • ScheduledExecutorService
        • Spring Task
        • Quartz
      • 交易系统
      • 权限设计
  • 编程语言
    • 编程语言
    • C & C++
    • Java
      • JVM
        • JVM Bytecode
      • Java 核心技术
      • Java 8 新特性
      • Java 集合框架
      • Java NIO
      • 并发编程
        • 线程生命周期与线程中断
        • 三个线程交替打印
        • 两个线程交替打印奇偶
        • 优雅终止线程
        • 等待通知机制
        • 万能钥匙:管程
        • 限流器
        • 无锁方案 CAS
    • Java 源码阅读
      • Unsafe
      • 异步计算 Future
      • Java Queue
      • CoalescingRingBuffer 分析
      • Java Collections
        • PriorityQueue 分析
        • HashMap 分析
        • TreeMap
    • Golang
    • Python
  • 框架/组件/类库
    • Guava
      • Guava Cache
      • Guava EventBus
    • RxJava
    • Apache MINA
    • Netty
      • 网络 IO 模型
      • Netty 生产问题
    • Apache Tomcat
    • MyBatis
    • 限流框架
    • Spring Framework
      • Spring Core
      • Spring 事务管理
    • Spring Boot
    • Spring Cloud
      • Feign & OpenFeign
      • Ribbon
      • Eurake
      • Spring Cloud Config
    • FixJ
    • Metrics
    • Vert.x
  • 中间件
    • Redis
      • Redis 基础
        • Redis 数据结构设计与实现
        • Redis 高性能网络模型
      • Redis checklist
      • 应用案例 - Redis 数据结构
      • 应用案例 - Redis 缓存应用
      • 应用案例 - Redis 集群
      • Redis 客户端
      • Redis 生产案例
        • [译] 在 Redis 中存储数亿个简单键值对
    • MySQL
      • MySQL 基础
      • MySQL Index
      • MySQL Transaction
      • MySQL 优化
      • MySQL 内核
      • MySQL Command
      • MySQL Checklist
      • MySQL Analysis Tool
      • 实现 MySQL
    • State Machine
    • 数据库连接池
    • MQ
      • 高性能内存队列 Disruptor
      • Kafka
      • Pulsar
      • RocketMQ
        • Broker 的设计与实现
      • NSQ
  • 实际案例
    • 线上 Case
      • Request Aborted
      • MySQL - Specified key was too long
      • Java 应用 CPU 100% 排查优化
      • 频繁 GC 导致的 Java 服务不响应
      • 导出优化
  • 大数据
    • 流计算
    • Flink
  • 其他
    • 工具
    • 读书
      • 设计数据密集型应用
      • 实现领域驱动设计
      • 精通比特币
      • 提问的智慧
    • 论文
    • 工程博客
    • 阅读源码
    • 面试
      • 如何在最短的时间里对对方有个全面的了解
    • 分享
    • 软技能
    • Todo
  • Blog
    • #算法
      • 查找
      • 位运算
      • 树
    • #架构
      • 1- 通信
    • Design & Dev & Opt
      • High Performance Data structure Design
  • Tiny Project
    • A Simple WeChat-like Instant Messaging Platform
由 GitBook 提供支持
在本页
  • Queue 源码分析
  • Priority Queue 源码分析

这有帮助吗?

  1. 编程语言
  2. Java 源码阅读
  3. Java Collections

PriorityQueue 分析

#Queue #优先队列

Queue 源码分析

Queue 的实现比较多

Priority Queue 源码分析

java.util.PriorityQueue 是优先级队列的实现,底层依赖的核心数据结构是二叉堆,支持插入元素、删除元素、peek、poll、查找等核心功能;现从以下几点进行分析:

  1. 使用的数据结构介绍

  2. 容量增长策略

  3. 核心API及分析(插入,删除,修改,查找/检查)

1. 使用的数据结构介绍

PriorityQueue 使用了二叉堆,堆是一个完全二叉树,且每个节点大于(或者小于)他的左右子节点。完全二叉树这种数据结构非常适合使用数组来存储,从 PriorityQueue 源码中也得到证实,使用了 transient Object[] queue; 作为存储元素的容器;所有对 PriorityQueue 的操作其实都是对这个二叉堆的操作,下面我们会分析各种操作的具体方法和复杂度。

使用数组存储时,位置在n的节点,他的两个自己点的位置分别为:2*n+1和2*(n+1)

2. 容量增长策略

public PriorityQueue();
public PriorityQueue(int initialCapacity);
public PriorityQueue(int initialCapacity, Comparator<? super E> comparator);
...

PriorityQueue 提供了数个构造方法,可以指定初始容量,如果不指定,默认是11;同时还可以指定 Comparator 接口,这个我们先忽略,这个只是用于比较两个对象的大小。

PriorityQueue 是使用数组存储的,那么容量是一个很关键的问题,分析可知,PriorityQueue 通过 grow 函数进行扩容,策略是:当前容量小于64时,每次翻倍;当前容量大于64时,每次增长50%;扩容的过程是:计算新的容量 -> 申请新的数组 -> copy到新数组。

3. 核心API及分析(插入,删除,修改,查找/检查)

  • 插入操作 add/offer

public boolean offer(E e) {
    // null 检查,直接抛出运行时异常
    if (e == null)
        throw new NullPointerException();
    modCount++;
    int i = size;
    // 判断数组容量,否则就扩容
    if (i >= queue.length)
        grow(i + 1);
    // 底层数组容量+1   
    size = i + 1;
    // 如果是第一个元素,放在第一个即可
    if (i == 0)
        queue[0] = e;
    // 不是第一个元素,进行堆化过程,以满足堆的特性
    else
        siftUp(i, e);
    return true;
}

// 堆化的插入过程, 这个过程是一个从下往上找的过程,直到堆顶或者中途结束
private void siftUpComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>) x;
    while (k > 0) {
        // 找到位置k的父节点的位置, (k - 1) >>> 1 是无符号右移操作,相当于 (k - 1) / 2
        int parent = (k - 1) >>> 1;
        // 暂存父节点位置的元素
        Object e = queue[parent];
        // 和父节点位置的元素比较一下,如果要插入的元素小于父节点,就退出
        if (key.compareTo((E) e) >= 0)
            break;
        // 否则,把父节点下移
        queue[k] = e;
        // 继续往上找
        k = parent;
    }
    // 找到了合适的插入位置,就把要插入的元素放进去
    queue[k] = key;
}

时间复杂度分析,核心操作是插入堆化,每次查找就除2,可以简单评估出 2 ^ k = n, 最大查找次数 k = logn, 所以时间复杂度是 O(logn); 由于整个过程中并没有占用额外空间,空间复杂度是 O(1)

  • 删除操作 remove/removeEq

// 定位指定元素,找到位置并返回
private int indexOf(Object o) {
    if (o != null) {
        for (int i = 0; i < size; i++)
            if (o.equals(queue[i]))
                return i;
    }
    return -1;
}

// 这个删除的核心方法,删除指定位置的元素,删除后需要堆化,这个过程会有些复杂
private E removeAt(int i) {
    // assert i >= 0 && i < size;
    modCount++;
    int s = --size;
    // 如果是删除最后一个元素,直接删掉
    if (s == i) // removed last element
        queue[i] = null;
    else {
        // 取最后一个元素暂存,并删除最后一个元素
        E moved = (E) queue[s];
        queue[s] = null;
        // 进行删除的堆化过程,以满足堆的特性,主要是将最后一个元素插入到合适位置
        siftDown(i, moved);
        // 这里的意思是,如果删除的是叶子节点,就走插入堆化的流程,可参考上面的分析
        if (queue[i] == moved) {
            siftUp(i, moved);
            if (queue[i] != moved)
                return moved;
        }
    }
    return null;
}
// 这个是从上到下的堆化过程,其中 k 是要删除的位置,x 是要插入的元素
private void siftDownComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>)x;
    // 为什么取一半? 主要是因为n个节点的堆,他的叶子节点的个数是 n/2, 如果是叶子节点的删除,只需要做
    // 从下到上的堆化过程即可,走插入流程;非叶子节点先进行从上到下的堆化
    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;
}

梳理下删除的流程就是: 1. 先找到删除元素的位置,不存在,就退出了 2. 取最后一个元素当作初始需要移动的数据,将这个元素插入到合适的位置(为什么是最后一个元素,我想是这样操作起来是最方便的把,不然,要保证是完全二叉树还是挺麻烦的) 3. 如果删除的元素是非叶子节点,就从他的子节点里找合适的节点往上移动,同时还要和取出来的最后一个元素比较,谁大谁就留在那个位置,就这样一直比较下去,直到叶子节点或者中途退出 4. 如果删除的是叶子节点,或者取出的最后一个元素比删除的元素的子节点都大(还需要看是不是比删除元素的父节点也大,所以需要向上做堆化),就需要做一个从下向上的插入堆化过程

  • poll peek contains

这些操作都比较简单: poll: 取堆顶元素,然后做一个从上到下的堆化过程,可以参考 remove 的 siftDownComparable 流程,时间复杂度O(logn) peek: 取数组第一个元素返回即可,时间复杂度O(1) contains: 遍历数组一遍,找是否有相等的元素,时间复杂度O(n)

到此 PriorityQueue 的核心功能基本分析完成。

上一页Java Collections下一页HashMap 分析

最后更新于4年前

这有帮助吗?