#优先级队列内部维护了一个堆的数据结构
1. 成员变量
/**
* 底层维护一个数组,其中queue[n]的左右两个孩子分别是
* queue[2*n+1]和queue[2*(n+1)](因为是从零开始的)。
* 最高优先级即比较后最小的元素在队首queue[0]。
*/
transient Object[] queue;
/**
* 优先级队列中元素的个数。
*/
private int size = 0;
/**
* 用来比较优先级的比较器。若为空,则采用元素的自然顺序。
* 比如数字的大小和字符串的字典序等。
*/
private final Comparator<? super E> comparator;
/**
* 优先级队列结构修改的次数。
* 主要用于iterator来判断是否并发修改而需要抛出
* ConcurrentModificationException异常。
*/
transient int modCount = 0;
2. 入队方法
1. public boolean offer(E e)
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
//修改次数增加
modCount++;
//新增元素在该位置
int i = size;
//如果越界,则需要扩容
//如果容量比较小(阈值64)则加倍,否则加50%。
if (i >= queue.length)
grow(i + 1);
//元素个数增加
size = i + 1;
//队列为空
if (i == 0)
queue[0] = e;
//元素加在末尾而向上调整
else
siftUp(i, e);
return true;
}
2.private void siftUp(int k, E 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) {
//key是即将要插入的数据
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)//大于等于0应该往后排,即优先级低
break;
//如果优先级比双亲节点高,双亲节点往后排
queue[k] = e;
//往上调整,双亲的位置成了当前位置
k = parent;
}
//将待插入的元素插入到合适位置
queue[k] = key;
}
3. 出队方法
1. public E poll()
@SuppressWarnings("unchecked")
public E poll() {
//队列为空返回null
if (size == 0)
return null;
//元素个数减少,找出队尾序号
int s = --size;
//修改次数增加
modCount++;
//保留队首元素
E result = (E) queue[0];
//保留队尾元素
E x = (E) queue[s];
//将队尾置空
queue[s] = null;
//如果队列非空,将队尾元素放到队首,则向下调整
if (s != 0)
siftDown(0, x);
//返回队首元素
return result;
}
2. private void siftDown(int k, E 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) {
//key为待调整元素
Comparable<? super E> key = (Comparable<? super E>)x;
//一半的地方
int half = size >>> 1; // loop while a non-leaf
//当非叶节点,循环
while (k < half) {
//待调整孩子序号,默认左孩子序号
int child = (k << 1) + 1; // assume left child is least
//待调整孩子,默认左孩子
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;
//如果key优先级低,那么c应该往前面交换
queue[k] = c;
//孩子节点变成当前节点,即往下调整
k = child;
}
//将待调整的元素插入到合适的位置
queue[k] = key;
}
4. 移除指定位置元素的方法
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);
/**此一情况偶尔出现,出现在调用iterator.remove的时候*/
//当调整完之后还位于待删除的地方,则说明队尾元素的优先级可能还要更高
if (queue[i] == moved) {
//向上调整
siftUp(i, moved);
//如果上浮了,返回队尾元素
if (queue[i] != moved)
return moved;
}
}
//其他情况返回空
return null;
}