1 简述
PriorityQueue是Java的数据结构中优先级队列,非线程安全。
理论上最大容量为Integer.MAX_VALUE - 8,但是添加的太多,可能会报OutOfMemoryError异常。
2 源码分析
2.1 构造
private static final int DEFAULT_INITIAL_CAPACITY = 11;
transient Object[] queue; // non-private to simplify nested class access
数据用数组的形式存储,默认初始大小为11。
2.2 offer
add同样走的offer方法。
public boolean add(E e) {
return offer(e);
}
public boolean offer(E e) {
// 添加null会抛空指针异常
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
if (i >= queue.length)
// 扩容一倍或者50%
grow(i + 1);
size = i + 1;
// 没有元素,放第一个
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}
// 扩容
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// Double size if small; else grow by 50%
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
// overflow-conscious code
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
queue = Arrays.copyOf(queue, newCapacity);
}
// 放元素,并保证逻辑上为完全二叉树
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}
整个添加元素的过程,就是先判断是否为null,是否需要扩容,然后插入元素到数组对应位置,保证逻辑上为完全二叉树。
2.3 siftUpUsingComparator
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;
}
该方法首先获取最后一个位置的父节点的索引,然后定义变量接受父节点的值,如果新增的节点值和父节点的值比较之后满足堆结构,则直接break返回,否则循环向上交换,最终完成堆结构调整。
2.4 peek
public E peek() {
return (size == 0) ? null : (E) queue[0];
}
总是取第一个元素,也就是顶堆最上面的父节点。
2.5 remove
public boolean remove(Object o) {
int i = indexOf(o);
if (i == -1)
return false;
else {
removeAt(i);
return true;
}
}
private E removeAt(int i) {
// assert i >= 0 && i < size;
modCount++;
int s = --size;
if (s == i) // removed last element
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;
}
如果是最后一个结点,直接置为null就行。
如果不是,会取出最后一个结点缓存在moved,然后删除最后一个结点,将最后一个结点的值覆盖在待删除结点位置,完成删除操作。
最后调整数组结构,使其符合完全二叉树!
poll和remove的逻辑基本一致。
总结
PriorityQueue本质还是一个FIFO的队列,其特殊之处在于他的出队顺序是按照优先级进行比较的。
至于这个优先级如何确定,需要结点去实现Comparator接口,提供比较大小的逻辑。