简介
PriorityQueue 一个基于优先级的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用的构造方法,每次出队的元素都是优先级最高的元素。该队列不允许使用 null 元素也不允许插入不可比较的对象(没有实现Comparable接口的对象)。
PriorityQueue 类
public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable
PriorityQueue 队列的头指排序规则最小的元素。如果多个元素都是最小值则随机选一个。
PriorityQueue 属性
// 默认初试长度
private static final int DEFAULT_INITIAL_CAPACITY = 11;
// 存储的元素
transient Object[] queue;
// 元素个数
private int size = 0;
// 比较器
private final Comparator<? super E> comparator;
// 修改次数
transient int modCount = 0;
// 数组最大长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
PriorityQueue 构造函数
// 空参构造函数
public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
// 带初始长度的构造函数
public PriorityQueue(int initialCapacity) {
this(initialCapacity, null);
}
// 使用外部比较器的构造函数
public PriorityQueue(Comparator<? super E> comparator) {
this(DEFAULT_INITIAL_CAPACITY, comparator);
}
// 带初始长度和外部比较器的构造函数
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
// 初始长度小于1则抛异常
if (initialCapacity < 1)
throw new IllegalArgumentException();
// 初始化时就会构建数组
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}
// 使用线性集合初始化
public PriorityQueue(Collection<? extends E> c) {
if (c instanceof SortedSet<?>) {
SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
this.comparator = (Comparator<? super E>) ss.comparator();
initElementsFromCollection(ss);
}
else if (c instanceof PriorityQueue<?>) {
PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
this.comparator = (Comparator<? super E>) pq.comparator();
initFromPriorityQueue(pq);
}
else {
this.comparator = null;
initFromCollection(c);
}
}
// 使用优先级队列初始化
public PriorityQueue(PriorityQueue<? extends E> c) {
this.comparator = (Comparator<? super E>) c.comparator();
initFromPriorityQueue(c);
}
// 使用有序SortedSet初始化
public PriorityQueue(SortedSet<? extends E> c) {
this.comparator = (Comparator<? super E>) c.comparator();
initElementsFromCollection(c);
}
// 使用集合初始化时调用此方法
private void initFromPriorityQueue(PriorityQueue<? extends E> c) {
if (c.getClass() == PriorityQueue.class) {
this.queue = c.toArray();
this.size = c.size();
} else {
initFromCollection(c);
}
}
// 当不是优先级队列初始化时,调用此方法
private void initElementsFromCollection(Collection<? extends E> c) {
Object[] a = c.toArray();
// If c.toArray incorrectly doesn't return Object[], copy it.
if (a.getClass() != Object[].class)
a = Arrays.copyOf(a, a.length, Object[].class);
int len = a.length;
if (len == 1 || this.comparator != null)
for (int i = 0; i < len; i++)
if (a[i] == null)
throw new NullPointerException();
this.queue = a;
this.size = a.length;
}
// 集合初始化时调用此方法
private void initFromCollection(Collection<? extends E> c) {
initElementsFromCollection(c);
heapify();
}
从成员变量和构造函数可以看出,优先级队列内部是数组实现,并且默认长度11,初始化时默认是空数组。随着不断向优先级队列添加元素,其容量会自动扩容,无需指定容量增加策略
PriorityQueue 扩容
private void grow(int minCapacity) {
// 获取数组长度
int oldCapacity = queue.length;
// 数组长度小于64时,新长度=原长度*2+2
// 数组长度大于等于64时,新长度=原长度*1.5
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
// 新长度是否越界
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 数组拷贝迁移
queue = Arrays.copyOf(queue, newCapacity);
}
// 越界时确定值
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
PriorityQueue 添加
public boolean add(E e) {
// 优先级队列为无界队列,认为不存在放满(实际是有界的)
return offer(e);
}
public boolean offer(E e) {
// 不允许放空值
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
// 元素个数大于等于数组长度时触发扩容
if (i >= queue.length)
grow(i + 1);
// 长度加1
size = i + 1;
// 扩容后长度为0(不可能存在)
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}
PriorityQueue 删除
public boolean remove(Object o) {
// 找到索引
int i = indexOf(o);
if (i == -1)
return false;
else {
// 正在删除
removeAt(i);
return true;
}
}
private E removeAt(int i) {
modCount++;
int s = --size;
// 是尾元素直接删除
if (s == i)
queue[i] = null;
else {
// 尾元素置空
E moved = (E) queue[s];
queue[s] = null;
// 尾元素从索引处开始下移
siftDown(i, moved);
if (queue[i] == moved) {
// 下移后不能平衡,需要上移
siftUp(i, moved);
if (queue[i] != moved)
return moved;
}
}
return null;
}
PriorityQueue 取值并删除
public E poll() {
// 队列为空返回null
if (size == 0)
return null;
// 长度减1
int s = --size;
modCount++;
// 取第一个元素
E result = (E) queue[0];
E x = (E) queue[s];
// 置空尾元素
queue[s] = null;
// 从第一位开始下移x
if (s != 0)
siftDown(0, x);
// 返回原来头元素
return result;
}
PriorityQueue 查询
public E peek() {
// 获取第一个元素
return (size == 0) ? null : (E) queue[0];
}
二叉堆排序
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}
private void siftUpComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = key;
}
private void siftUpUsingComparator(int k, E x) {
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (comparator.compare(x, (E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = x;
}
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
private void siftDownComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>)x;
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
int right = child + 1;
if (right < size &&
((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
c = queue[child = right];
if (key.compareTo((E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = key;
}
private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
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;
}
二叉堆看起来比较绕,我先普及一下二叉堆。父节点的键值总是小于或等于任何一个子节点的键值,二叉堆分为最大堆和最小堆
1、最大堆:父节点的键值总是大于或等于任何一个子节点的键值;
2、最小堆:父节点的键值总是小于或等于任何一个子节点的键值。
二叉堆又可以用数组来表示,基于数组实现的二叉堆,对于数组中任意位置的n上元素,其左孩子在[2n+1]位置上,右孩子[2(n+1)]位置,它的父亲则在[(n-1)/2]上,而根的位置则是[0]。
二叉堆只有上移下移操作,jdk中在出队时,不是直接将根元素删除,然后再将下面的元素做上移,重新补充根元素;而是找出队尾的元素,并在队尾的位置上删除,然后通过根元素的下移,给队尾元素找到一个合适的位置,最终覆盖掉根元素,从而达到删除根元素的目的。这样做在一些情况下,会比直接删除在上移根元素,或者直接下移根元素再调整队尾元素的位置少操作一些步奏