priorityqueue默认使用自然排序,即元素实现comparable接口的compareto()方法;若未实现则抛classcastexception,自定义排序需传入comparator。

PriorityQueue 默认用什么排序?
它不按你想象的“数字小就优先”排,而是用 Comparable 接口的 compareTo() 方法——也就是自然排序。整数、字符串、日期这些自带比较逻辑的类型,能直接用;但自定义类必须实现 Comparable,否则一加就抛 ClassCastException。
常见错误现象:Exception in thread "main" java.lang.ClassCastException: MyClass cannot be cast to java.lang.Comparable
- 没写
implements Comparable<myclass></myclass>就往PriorityQueue里塞对象 - 写了但
compareTo()返回值逻辑反了(比如该返回负数时返回正数),导致最小/最大堆行为错乱 - 混用
null元素:默认队列不允许null,加了直接NullPointerException
怎么让 PriorityQueue 按指定字段或逆序排?
靠构造时传 Comparator,不是改元素本身。比如想按 age 升序,但 Person 类没实现 Comparable,或者你想临时按 name 降序——这时候必须显式给比较器。
性能影响:每次入队/出队都要调用 compare(),如果逻辑太重(比如里面做数据库查询),会明显拖慢堆调整速度。
- 升序:
new PriorityQueue(Comparator.comparing(p -> p.age)) - 降序:
new PriorityQueue(Comparator.comparing(p -> p.age).reversed()) - 多级排序:
Comparator.comparing(p -> p.age).thenComparing(p -> p.name) - 注意:
Comparator.nullsFirst()或nullsLast()要主动加,否则null还是会崩
peek()、poll()、remove() 三个操作到底动不动堆?
peek() 只看堆顶,不破坏结构;poll() 拿走堆顶并重堆,O(log n);remove(Object) 是线性扫描 + 删除 + 重堆,O(n) —— 它不是按优先级删,而是按值删,且删完还要重新调整整个堆。
容易踩的坑:
- 误以为
remove(x)是“删优先级最低的 x”,其实它删的是第一个equals(x)为true的节点,和优先级无关 - 在循环里反复调用
remove()清理过期任务?复杂度爆炸,应改用延迟删除(如维护一个Set记录已失效 ID)或换TreeSet -
poll()在空队列返回null,不是抛异常;但remove()在找不到时也返回false,别和poll()的语义混淆
PriorityQueue 真的是二叉堆?底层怎么存的?
是,但不是用指针连的树,而是用数组模拟完全二叉树:索引 0 是根,节点 i 的左子是 2*i+1,右子是 2*i+2,父是 (i-1)/2。所以它不保证遍历顺序是排序结果,toArray() 或增强 for 得到的是数组原始布局,不是有序序列。
兼容性注意:这个存储方式从 JDK 1.5 到 21 都没变,但别依赖下标算位置——这是实现细节,官方不承诺稳定。
- 想按优先级全取出来?只能反复
poll(),不能直接遍历queue.toArray() - 扩容机制:初始容量 11,满后按
oldCapacity + (oldCapacity 扩(即 ×1.5),频繁扩容会影响吞吐 - 没有线程安全版本,多线程要套
Collections.synchronizedQueue()或直接用PriorityBlockingQueue










