PriorityQueue底层是小根堆,用数组模拟完全二叉树,通过siftUp和siftDown维护堆序,仅保证堆顶最值和父子大小关系,不保证整体有序,适用于Top-K等需局部最优的场景。

PriorityQueue 并不直接用于堆排序,但它和堆排序共享同一底层数据结构——堆。它通过动态维护一个“随时可取最值”的数组结构,实现对变量有序状态的实时管理。
PriorityQueue 的底层是小根堆(默认)
Java 中的 PriorityQueue 默认按升序逻辑组织元素,即堆顶始终是最小元素。它用数组模拟完全二叉树,遵循以下下标关系:
- 父节点下标 = (子节点下标 − 1) / 2
- 左子节点下标 = 父节点下标 × 2 + 1
- 右子节点下标 = 父节点下标 × 2 + 2
这种存储方式无需指针、空间紧凑,且支持 O(1) 定位父子/兄弟节点,是高效维护有序性的基础。
插入时向上调整(siftUp),保持堆性质
每次调用 offer() 或 add(),新元素被追加到数组末尾,然后从该位置出发向上比较、交换,直到满足小根堆条件(父 ≤ 子):
- 若新元素比父节点小,就与父节点交换
- 继续向上检查新的父节点,直到到达堆顶或不再违反堆序
- 整个过程最多上浮 log₂n 层,时间复杂度为 O(log n)
例如插入 5 到已有堆 [10, 20, 15](对应树形:10 为根,20/15 为左右子),5 先放在末尾变成 [10, 20, 15, 5],再逐层上浮至堆顶,最终形成 [5, 10, 15, 20]。
出队时向下调整(siftDown),维持结构完整
调用 poll() 时,并非真正删除堆顶,而是:
- 记录堆顶值(最小元素)
- 将数组最后一个元素移到堆顶位置
- 从堆顶开始向下调整:比较当前节点与两个子节点,选较小者交换;重复直到叶节点或满足小根堆性质
这个“尾部填顶 + 向下筛”的策略避免了数组搬移,保证了 O(log n) 删除效率,同时始终让最小值处于索引 0 位置。
不排序整个集合,只保证“顶端有序”
PriorityQueue 的核心目标不是像堆排序那样输出全局有序序列,而是持续提供 O(1) 访问最值 + O(log n) 维护能力。它不要求整个数组单调,只要求满足堆序约束。因此:
- 内部数组不是排好序的(如 [1, 3, 2, 7, 6, 5, 4] 是合法小根堆,但非升序)
- 只有 poll() 或 peek() 能稳定拿到当前最小值,其他位置无序
- 适合 Top-K、任务调度、延迟队列等只需“局部最优”的场景
堆排序则是在此基础上多走一步:反复 poll + 放回末尾 + 缩小堆范围,最终把整个数组变成完全有序——而 PriorityQueue 本身不执行这一步。

















