java中priorityqueue内部用动态数组实现最小堆,通过索引公式(左=2i+1、右=2i+2、父=(i-1)/2)映射完全二叉树,插入时上浮、删除时下沉维持堆序,初始容量11,扩容为1.5倍,全程无递归、无指针,高效内存友好。

Java 中 PriorityQueue 内部确实是用动态数组(Object[])实现的完全二叉堆,不是链式结构,也不依赖外部库。它默认是最小堆,通过数组索引关系维护堆序性,同时自动扩容保证容量弹性。
数组如何表示二叉堆
完全二叉树天然适合用数组存储:根节点在索引 0,对任意节点索引 i:
- 左孩子索引 = 2 * i + 1
- 右孩子索引 = 2 * i + 2
- 父节点索引 = (i - 1) / 2(整除)
比如索引 0 的根,左孩子在 1,右孩子在 2;索引 1 的节点,父节点是 (1−1)/2 = 0,左孩子是 3,右孩子是 4 —— 全部是纯算术,无指针开销。
插入时怎么维持堆性质
新元素加到数组末尾(逻辑上的“最右下位置”),然后不断与父节点比较、上浮(sift-up):
- 若比父小(最小堆),就交换;继续向上检查
- 直到到达根,或满足父 ≤ 当前值
- 时间复杂度 O(log n),因为最多上浮树高次
例如插入 3 到已有 [1,4,5](对应树形:1 为根,4/5 为左右子),数组变为 [1,4,5,3];3 在索引 3,父是索引 1 的 4,3
出队(poll)怎么保持结构完整
总是取索引 0 的最小值,然后把末尾元素移到根位置,再向下调整(sift-down):
- 将最后一个元素移到索引 0
- 比较它与两个孩子的大小,选较小孩子交换
- 重复直到该节点≤两个孩子,或已无孩子
- 数组长度减一,逻辑大小收缩
注意:只和孩子比,不和父比;下沉路径唯一,靠局部比较驱动。
动态扩容机制怎么工作
初始容量是 11,当数组满时触发扩容:
- 新容量 = oldCapacity + (oldCapacity >> 1)(即 1.5 倍)
- 使用
Arrays.copyOf()复制元素,旧数组被丢弃 - 扩容只影响存储,不影响堆逻辑 —— 索引映射规则不变
扩容不改变堆结构,只是换了个更大的“容器”,所有节点仍按相同索引公式定位。
整个实现没有递归、不建节点对象,纯数组+位运算+循环,兼顾性能与内存友好。理解索引映射和上浮/下沉逻辑,就抓住了 PriorityQueue 的核心。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











