Java中PriorityQueue默认为小顶堆,要实现大顶堆需在构造时传入翻转比较逻辑的Comparator,如(a,b)->b-a;已存在元素不会重排,运行时不可切换Comparator。

PriorityQueue默认是小顶堆,不是大顶堆
Java的PriorityQueue底层用的是最小堆,所以直接new出来的实例,peek()拿到的是最小元素。想让它变成“最大值优先”,不能靠改内部结构,只能从比较逻辑入手——也就是重写Comparator,让大小关系翻个个儿。
重写Comparator时别直接用Collections.reverseOrder()套Integer/Long
看起来最省事:用Collections.reverseOrder(),但要注意类型擦除和泛型边界。如果队列存的是基本类型包装类(比如Integer),它能工作;但一旦你放的是自定义对象,或者用了原始类型数组做中转,就容易抛ClassCastException或根本不起作用。
更稳的做法是手写lambda或匿名Comparator:
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
这个写法安全、直观,也避开了自动装箱比较可能引发的溢出问题(比如Integer.MAX_VALUE - Integer.MIN_VALUE)。如果必须用方法引用,确保类型明确:
立即学习“Java免费学习笔记(深入)”;
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
用Comparator翻转后,add()和poll()行为完全符合大顶堆预期
翻转之后,所有操作语义不变,只是“优先”标准变了:add()仍为O(log n),poll()仍弹出当前最大值,peek()返回最大值而非最小值。不需要额外维护、也不用自己实现上浮下沉。
- 插入顺序不影响堆序,只看比较结果
- 如果多个元素相等,它们之间的相对顺序不保证(
PriorityQueue不保证稳定) - 注意null值:默认Comparator不允许null,手写lambda里要自己判空,否则add时抛
NullPointerException
别在运行时动态切换Comparator
PriorityQueue的Comparator是在构造时绑定的,创建后无法替换。试图重新赋值comparator()返回的对象,或用反射去改,不仅无效,还会破坏堆结构,导致后续poll()返回错误值甚至死循环。
如果业务需要两种排序策略共存,就建两个独立队列:
PriorityQueue<Integer> minHeap = new PriorityQueue<>();<br>PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
混用同一个实例去“临时改规则”,是常见但不可行的思路。
真正容易被忽略的是:Comparator翻转只影响新插入元素的排序位置,对已存在的元素不做重排。所以不要在非空队列上试图“切换模式”——那只会让堆结构错乱。










