PriorityQueue 源码分析
Posted zhuxudong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PriorityQueue 源码分析相关的知识,希望对你有一定的参考价值。
PriorityQueue
1)基于优先级堆实现的无界优先级队列,优先级队列的元素根据自然顺序或指定的比较器进行排序。
2)优先级队列不允许使用 null 元素,并且元素必须实现 Comparable 接口。
3)PriorityQueue 的头元素是排序最小的元素,存在多个最小的头元素时,随机选取其中的一个。
4)PriorityQueue 不是线程安全的,如果需要并发访问,请使用 PriorityBlockingQueue。
创建实例
/**
* 默认初始容量
*/
private static final int DEFAULT_INITIAL_CAPACITY = 11;
/**
* 用于构建平衡二叉堆的底层数组
* 父节点:queue[n]
* 左子节点:queue[2*n+1]
* 右子节点:queue[2*(n+1)]
* 计算父节点:(n-1)/2
*/
transient Object[] queue; // non-private to simplify nested class access
/**
* 优先级队列中的元素个数
*/
int size;
/**
* 排序元素的比较器,如果为 null,则根据元素的自然顺序排序
*/
private final Comparator<? super E> comparator;
/**
* PriorityQueue 被结构化修改的次数,用于实现 Fast-Fail
*/
transient int modCount; // non-private to simplify nested class access
/**
* 创建一个初始容量为 11,
* 队列元素根据自然顺序排序的空 PriorityQueue
*/
public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
/**
* 创建一个初始容量为 initialCapacity,
* 队列元素根据自然顺序排序的空 PriorityQueue
*/
public PriorityQueue(int initialCapacity) {
this(initialCapacity, null);
}
/**
* 创建一个初始容量为 11,
* 队列元素根据指定比较器 comparator 排序的空 PriorityQueue
*/
public PriorityQueue(Comparator<? super E> comparator) {
this(DEFAULT_INITIAL_CAPACITY, comparator);
}
/**
* 创建一个初始容量为 initialCapacity,
* 队列元素根据指定比较器 comparator 排序的空 PriorityQueue
*/
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
// Note: This restriction of at least one is not actually needed,
// but continues for 1.5 compatibility
if (initialCapacity < 1) {
throw new IllegalArgumentException();
}
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}
往优先级队列中添加元素:add、offer
/**
* 往优先级队列中插入元素
*/
@Override
public boolean add(E e) {
return offer(e);
}
/**
* 插入元素到 PriorityQueue 中
*/
@Override
public boolean offer(E e) {
if (e == null) {
throw new NullPointerException();
}
modCount++;
// 读取元素个数
final int i = size;
// 元素个数超出队列容量
if (i >= queue.length) {
// 则进行扩容
grow(i + 1);
}
// 插入元素并平衡二叉堆结构
siftUp(i, e);
// 递增容量
size = i + 1;
return true;
}
/**
* The maximum size of array to allocate.
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// 读取旧容量
final int oldCapacity = queue.length;
/**
* 1)旧容量 < 64,执行【双倍+2】扩容
* 2)执行 1.5 倍向下取整扩容
*/
int newCapacity = oldCapacity + (oldCapacity < 64 ?
oldCapacity + 2 :
oldCapacity >> 1);
// 新容量超出 Integer.MAX_VALUE - 8
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = PriorityQueue.hugeCapacity(minCapacity);
}
// 拷贝源数组
queue = Arrays.copyOf(queue, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 容量溢出
if (minCapacity < 0) {
throw new OutOfMemoryError();
}
/**
* 1)minCapacity = MAX_ARRAY_SIZE,则返回 MAX_ARRAY_SIZE
* 2)否则返回 Integer.MAX_VALUE
*/
return minCapacity > MAX_ARRAY_SIZE ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
/**
* 在索引 k 处插入元素 x,通过向上提升 x 直到它大于等于它的父节点或直到根节点来维持堆不变。
*/
private void siftUp(int k, E x) {
if (comparator != null) {
// 使用指定的比较器执行提升操作
siftUpUsingComparator(k, x);
} else {
// 使用自然顺序执行提升操作
siftUpComparable(k, x);
}
}
@SuppressWarnings("unchecked")
private void siftUpComparable(int k, E x) {
final Comparable<? super E> key = (Comparable<? super E>) x;
// 索引为 0 时,当前元素就是根
while (k > 0) {
// 计算父节点索引
final int parent = k - 1 >>> 1;
// 读取父元素值
final Object e = queue[parent];
// 当前元素大于等于父元素,则不需要额外的操作来维持二叉堆结构,可以直接退出
if (key.compareTo((E) e) >= 0) {
break;
}
// 将父元素下移
queue[k] = e;
// 递归处理祖父节点
k = parent;
}
// 将当前元素插入到目标位置
queue[k] = key;
}
@SuppressWarnings("unchecked")
private void siftUpUsingComparator(int k, E x) {
while (k > 0) {
final int parent = k - 1 >>> 1;
final Object e = queue[parent];
if (comparator.compare(x, (E) e) >= 0) {
break;
}
queue[k] = e;
k = parent;
}
queue[k] = x;
}
查看堆顶元素
/**
* 读取堆顶元素,最小值
*/
@Override
@SuppressWarnings("unchecked")
public E peek() {
/**
* 1)优先级队列为空,则防护 null
* 2)返回索引为 0 的堆顶元素
*/
return size == 0 ? null : (E) queue[0];
}
读取并移除堆顶元素
/**
* 读取并移除堆顶元素
*/
@Override
@SuppressWarnings("unchecked")
public E poll() {
if (size == 0) {
return null;
}
final int s = --size;
modCount++;
final E result = (E) queue[0];
final E x = (E) queue[s];
queue[s] = null;
if (s != 0) {
siftDown(0, x);
}
return result;
}
/**
* 在索引 k 处插入元素 x,通过向下移动 x 直到它小于等于它的子节点或它本身就是子节点
* 为止,来维持堆不变。
*/
private void siftDown(int k, E x) {
if (comparator != null) {
siftDownUsingComparator(k, x);
} else {
siftDownComparable(k, x);
}
}
@SuppressWarnings("unchecked")
private void siftDownComparable(int k, E x) {
final Comparable<? super E> key = (Comparable<? super E>)x;
// 计算二分节点索引
final int half = size >>> 1; // loop while a non-leaf
// 目标索引在二分节点前【如果 k >= half 则表示 k 在数组中没有子节点,无需处理】
while (k < half) {
// 计算左子节点索引
int child = (k << 1) + 1; // assume left child is least
// 读取左子节点值
Object c = queue[child];
// 计算右子节点索引
final int right = child + 1;
// 读取左、右子节点的最小值
if (right < size &&
((Comparable<? super E>) c).compareTo((E) queue[right]) > 0) {
// 左子节点值 > 右子节点值,则读取右子节点值
c = queue[child = right];
}
// 目标值小于等于 c,则已经是升序排序,直接退出
if (key.compareTo((E) c) <= 0) {
break;
}
/**
* 目标值大于 c,将最小的子节点值放在父节点上
*/
queue[k] = c;
// 更新索引为值小的子节点索引
k = child;
}
// 插入目标值
queue[k] = key;
}
@SuppressWarnings("unchecked")
private void siftDownUsingComparator(int k, E x) {
final int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
final int right = child + 1;
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0) {
c = queue[child = right];
}
if (comparator.compare(x, (E) c) <= 0) {
break;
}
queue[k] = c;
k = child;
}
queue[k] = x;
}
清空优先级队列
/**
* 移除优先级队列中的所有元素
*/
@Override
public void clear() {
modCount++;
for (int i = 0; i < size; i++) {
queue[i] = null;
}
size = 0;
}
读取元素个数
@Override
public int size() {
return size;
}
以上是关于PriorityQueue 源码分析的主要内容,如果未能解决你的问题,请参考以下文章