priorityqueue默认是最小堆,队首为最小元素;需传comparator.reverseorder()实现最大堆,自定义类须实现comparable或传comparator,且非线程安全。

PriorityQueue 默认是最小堆,别默认以为它能自动倒序
Java 的 PriorityQueue 默认按自然顺序排序,也就是最小堆:队首(peek())永远是**最小元素**。想让它变成最大堆?不能靠改内部逻辑,得靠传入自定义比较器——这是最常被忽略的前提。
常见错误现象:PriorityQueue<integer> pq = new PriorityQueue(); pq.add(3); pq.add(1); pq.add(2); System.out.println(pq.poll());</integer> 输出 1,有人误以为“没生效”或“要手动排序”,其实它工作完全正常,只是你没意识到它天生就是最小堆。
- 自然类型(
Integer、String等)直接用空构造函数即可实现最小堆 - 自定义类必须实现
Comparable接口,否则运行时抛ClassCastException - 不建议依赖
PriorityQueue的迭代器顺序——它不保证遍历有序,只保证poll()/peek()正确
用 Comparator.reverseOrder() 快速实现最大堆
对基本包装类(如 Integer、Double),最简方式是传 Comparator.reverseOrder()。它本质是调用 compareTo() 后取反,开销极小,且可读性高。
使用场景:需要频繁取最大值(比如 Top-K 问题、滑动窗口最大值的简化版)、模拟最大堆语义但又不想写完整比较逻辑。
立即学习“Java免费学习笔记(深入)”;
示例:
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder()); maxHeap.offer(3); maxHeap.offer(1); maxHeap.offer(2); System.out.println(maxHeap.poll()); // 输出 3
- 不要用
new PriorityQueue((a, b) -> b - a)处理大整数——可能溢出;reverseOrder()安全 - 如果元素为
null,reverseOrder()会抛NullPointerException,注意判空或用Comparator.nullsLast()组合 - 这个写法在 Java 8+ 可用,低版本需手写匿名类
自定义类怎么进 PriorityQueue:Comparable vs Comparator 二选一
自定义类进 PriorityQueue,必须明确排序依据。要么让类自己实现 Comparable,要么在构造时传 Comparator——二者只能选其一,混用会覆盖前者,且容易引发逻辑混乱。
参数差异明显:new PriorityQueue<someclass>()</someclass> 要求 SomeClass implements Comparable<someclass></someclass>;而 new PriorityQueue(Comparator.comparing(...)) 则完全绕过类本身的 compareTo 方法。
- 如果类天然有主排序逻辑(如
Task按优先级排),优先实现Comparable - 如果同一类需在不同场景按不同字段排序(比如一会儿按时间、一会儿按权重),必须用外部
Comparator,避免污染领域模型 - 实现
Comparable时,务必保证compareTo与equals一致,否则contains()行为可能不符合预期
PriorityQueue 不是线程安全的,多线程下 poll/offer 可能出错
所有操作(offer()、poll()、peek())都不加锁。并发修改会导致 ConcurrentModificationException 或静默数据错乱——这不是偶发 bug,是设计使然。
性能影响:加同步块(synchronized)会严重拖慢吞吐;用 PriorityBlockingQueue 是更合理的选择,它基于可重入锁 + 条件队列,支持阻塞式操作,且保持堆性质。
- 别试图用
Collections.synchronizedCollection(new PriorityQueue())——它只同步单个方法,无法保证复合操作(如if (!pq.isEmpty()) pq.poll())原子性 -
PriorityBlockingQueue不接受null元素,这点和PriorityQueue一致 - 如果业务允许“最终一致性”,也可考虑用无锁结构(如
LockFreePriorityQueue第三方库),但得自行承担复杂度
事情说清了就结束。真正难的不是写对一行 new PriorityQueue(Comparator.reverseOrder()),而是想清楚:这个堆谁在读、谁在写、是否跨线程、排序依据会不会随需求变化——这些才是后期翻车的高发区。










