- PriorityQueue,我用过的场景是用来构建大小堆。
- PriorityQueue的排序是在创建时就制定的Comparable或Comparator。
- PriorityQueue不允许添加null元素。
- 如果队列中插入Comparable或Comparator无法比较的元素时,会抛出ClassCastException异常。
- 队列头是队列的最后一个元素。如果最后一个值有多个元素的话,队列头是任意一个元素。
- 队列可通过poll,remove,peek,element方法获取队列头,即队列的最后一个元素。
- PriorityQueue的长度没有限制,内部变量capacity用于管理队列长度,当添加元素时,容量会自动增加。
- PriorityQueue不是同步的,如果一个线程在修改PriorityQueue的话,其他线程无法获取该PriorityQueue。在这种情况下,考虑线程安全的类:java.util.concurrent.PriorityBlockingQueue
- 时间复杂度是O(log(n))的方法:offer,poll,remove(),add
- 时间复杂度是O(n)的方法:remove(Object),contains(Object)
- 时间复杂度是O(1)的方法:peek,element,size
PriorityQueue实现了java.io.Serializable接口,指定了serialVersionUID
PriorityQueue声明了默认的容量DEFAULT_INITIAL_CAPACITY = 11;
transient关键字无法被序列化
final关键字:
- 修饰类:该类不可以被继承
- 修饰方法:该方法不可以被重写
- 修饰变量:该变量不可以被修改
-
Comparator用private final 修饰,使得Comparator只可以在初始化时被指定,且不可以在其他时候修改。
private final Comparator<? super E> comparator; -
在初始化优先队列的容量时,最小是1,否则抛出IllegalArgumentException。
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;
}
-
PriorityQueue可通过Collection的实现类构建,前提是实现类是
SortedSet的子类或者是PriorityQueue的实例,也就是本身就有comparator的实例,否则无法创建。 -
创建实例时,将Collection的实现类使用toArray()转换为数组,并遍历,判断有没有null元素。
-
优先队列的最大size如下:
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
以为部分虚拟机要在数组头存储东西,所以为了避免爆内存-8. -
PriorityQueue实现的基础是平衡二叉堆,存储在数组中,元素queue[n] 的左孩子和右孩子分别为queue[2n+1] 、queue[2(n+1)]
-
因为上述存储方式,PriorityQueue头就是0位元素,所以返回数组的第0位。
public E peek() { return (size == 0) ? null : (E) queue[0];}
/>>>表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0
- 添加元素时,用到offer函数,offer函数调用siftUpUsingComparator函数,siftUpUsingComparator函数的k是添加该元素之后的队列的长度,x是该元素,如果想在第k位存储元素,则该元素的父结点为m位的元素,2*m+1=k, m=(k- 1)/2 ,如果该元素大于等于父元素,则直接保存,否则把父元素写在当前的位置上,再找当前元素的父元素,因此offer的时间复杂度是O(log(n))。
如果调用compare方法大于0,就把前一个数和后一个数交换,也就是把大的数放后面了
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;
}
- poll方法直接把最后一位赋为空,并传入最后一位元素到siftDown函数中。
public E poll() {
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;
}
- siftDown方法调用siftDownComparable函数,对size除2,size是原来的size0-1之后的结果,half=(size0-1)/2,即half所在的元素是第size0位的父结点。k是0,如果k小于half,则证明还没有把父结点填充上,需要获取k的左孩子c,如果左孩子大于右孩子,则把child指向右孩子,并把有孩子赋值给c用c去替换父节点,直到替换到跟节点。
private void siftDownComparable(int k, E x) {
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;
queue[k] = c;
k = child;
}
queue[k] = key;
}
- contains要遍历一边数组,所以是O(n)