【Java】PriorityQueue详解:从使用到底层原理全解析
大家好,我是云扬~ 今天想和大家深入聊聊 Java 中的 PriorityQueue,这个基于优先级堆的优先队列实现,在日常开发和算法场景中都特别实用。它最特别的地方在于打破了普通队列 “先进先出” 的规则,能按照元素优先级自动排序,插入和删除操作的时间复杂度仅为 O (log n),效率很高。
一、PriorityQueue 简介
PriorityQueue 是 Java 集合框架中的重要成员,底层基于优先级堆实现,核心作用是维护元素的优先级顺序。当我们插入元素时,它会自动将元素放到合适的位置;取出元素时,始终是优先级最高的元素先出队。这一特性让它在任务调度、事件处理、算法实现(比如 Dijkstra、Prim 算法)等场景中大放异彩。
二、PriorityQueue 实战使用
1. 基础使用流程
首先我们来看最基本的创建、添加、遍历操作,代码示例如下:
import java.util.PriorityQueue;
public class PriorityQueueDemo {
public static void main(String[] args) {
// 1. 创建PriorityQueue对象(默认小顶堆,自然排序)
PriorityQueue<String> priorityQueue = new PriorityQueue<>();
// 2. 添加元素(offer()方法,插入失败返回false)
priorityQueue.offer("MySQL");
priorityQueue.offer("云扬");
priorityQueue.offer("Java编程");
// 3. 遍历并打印元素(poll()方法取出并删除队首元素)
System.out.println("默认优先级遍历结果:");
while (!priorityQueue.isEmpty()) {
System.out.print(priorityQueue.poll() + " ");
}
// 输出结果:Java编程 MySQL 云扬(按字符串自然排序)
}
}
2. 指定优先级顺序
默认情况下,PriorityQueue 是小顶堆(自然排序),如果我们需要大顶堆或者自定义排序规则,可以通过传入Comparator实现:
import java.util.Comparator;
import java.util.PriorityQueue;
public class PriorityQueueCustomDemo {
public static void main(String[] args) {
// 创建大顶堆(反转自然排序)
PriorityQueue<String> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
maxHeap.offer("MySQL");
maxHeap.offer("云扬");
maxHeap.offer("Java编程");
System.out.println("大顶堆遍历结果:");
while (!maxHeap.isEmpty()) {
System.out.print(maxHeap.poll() + " ");
}
// 输出结果:云扬 MySQL Java编程 (按字符串反转排序)
// 自定义对象排序(以User的age为例)
PriorityQueue<User> userHeap = new PriorityQueue<>(Comparator.comparingInt(User::getAge));
userHeap.offer(new User("1","张三", 25));
userHeap.offer(new User("2","李四", 20));
userHeap.offer(new User("3","王五", 30));
System.out.println("\n自定义对象排序结果:");
while (!userHeap.isEmpty()) {
User user = userHeap.poll();
System.out.print(user.getName() + "(" + user.getAge() + ") ");
}
// 输出结果:李四(20) 张三(25) 王五(30)
}
static class User {
private String id;
private String name;
private int age;
public User(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public String getId() { return id; }
public String getName() { return name; }
public int getAge() { return age; }
}
}
三、底层实现原理
1. 堆结构基础
PriorityQueue 的底层是完全二叉树实现的堆,默认是小顶堆(每个节点的值≤其子节点的值)。堆的存储非常高效,直接使用数组即可,无需额外指针:
- 父节点下标:
(i - 1) / 2(i 为当前节点下标) - 左子节点下标:
2 * i + 1 - 右子节点下标:
2 * i + 2
比如数组[a, b, c, d, e]对应的堆结构:
a (0) // 根节点
/ \
b(1) c(2) // 第二层
/ \
d(3) e(4) // 第三层
2. 核心方法剖析
PriorityQueue 的核心方法都围绕堆的调整(上浮 siftUp、下沉 siftDown)展开,我们逐一解析:
(1)添加元素:add () vs offer ()
两者语义一致,都是插入元素,区别仅在于插入失败时的处理:
add(E e):插入失败抛出IllegalStateExceptionoffer(E e):插入失败返回false(更推荐使用)
底层实现逻辑:先扩容(如果数组已满),再将元素插入数组末尾,最后通过siftUp调整堆结构,保证堆的性质。关键源码片段(简化):
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;
}
(2)获取元素:element () vs peek ()
两者都用于获取队首元素(不删除),区别在于队列为空时的处理:
element():抛出NoSuchElementExceptionpeek():返回null(更推荐使用)
底层直接返回数组 0 下标处的元素(堆顶):
public E peek() {
return (size == 0) ? null : (E) queue[0];
}
(3)删除元素:remove () vs poll ()
两者都用于获取并删除队首元素,区别在于队列为空时的处理:
remove():抛出NoSuchElementExceptionpoll():返回null(更推荐使用)
底层逻辑:取出堆顶元素,将数组末尾元素移到堆顶,再通过siftDown调整堆结构:
public E poll() {
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;
}
(4)删除指定元素:remove (Object o)
用于删除队列中与指定对象相等的元素,删除后需要调整堆结构,时间复杂度为 O (n)(需先遍历找到元素):
public boolean remove(Object o) {
int i = indexOf(o);
if (i == -1)
return false;
else {
removeAt(i); // 调整堆结构
return true;
}
}
四、总结
PriorityQueue 是 Java 中处理优先级元素的利器,核心要点总结如下:
- 底层基于堆(完全二叉树)实现,使用数组存储,效率高;
- 默认小顶堆,支持通过 Comparator 自定义排序规则;
- 插入 / 删除元素时间复杂度 O (log n),查询堆顶元素 O (1);
- 常用方法推荐使用
offer()、peek()、poll(),避免抛出异常; - 适用于任务调度、算法实现等需要按优先级处理元素的场景。
如果需要使用 PriorityQueue 存储自定义对象,记得让对象实现Comparable接口,或者在创建队列时传入Comparator,否则会抛出ClassCastException哦~ 大家在实际开发中有没有用过 PriorityQueue?欢迎在评论区分享你的使用场景!



