PriorityQueue
1 初始化
PriorityQueue优先级队列,可以为队列里面的元素定义优先级别,从类定义上来看,它具备基础队列的功能,同时可以进行序列化。transient Object[] queue
存储队列元素、size(存储元素大小)、comparator(排序规则)、modCount(纪录修改的次数)
public class PriorityQueue<E> extends AbstractQueue<E>
implements java.io.Serializable {
private static final int DEFAULT_INITIAL_CAPACITY = 11;
/**
* Priority queue represented as a balanced binary heap: the two
* children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The
* priority queue is ordered by comparator, or by the elements'
* natural ordering, if comparator is null: For each node n in the
* heap and each descendant d of n, n <= d. The element with the
* lowest value is in queue[0], assuming the queue is nonempty.
*/
transient Object[] queue; // non-private to simplify nested class access
/**
* The number of elements in the priority queue.
*/
private int size = 0;
/**
* The comparator, or null if priority queue uses elements'
* natural ordering.
*/
private final Comparator<? super E> comparator;
/**
* The number of times this priority queue has been
* <i>structurally modified</i>. See AbstractList for gory details.
*/
transient int modCount = 0; // non-private to simplify nested class access
/**
* Creates a {@code PriorityQueue} with the default initial
* capacity (11) that orders its elements according to their
* {@linkplain Comparable natural ordering}.
*/
public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
}
2 入队列
offer方法主要是插入队列,基本都是普通的数组操作,这里重点的是两个方法grow和siftUp。
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
//判断size是否大于queue.length,如果大于就调用grow
if (i >= queue.length)
grow(i + 1);
size = i + 1;
//如果原有队列的size是0那么就直接插入,否则调用siftUp
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}
grow方法
这里的grow跟ArrayList的grow方法不太一样,不一样的地方是增长因子不一样,这里做了个优化。只有大于64的时候采用增加50%的策略
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// Double size if small; else grow by 50%
//判断当大于64的时候采用增加50%这种策略,也就是跟ArrayList一样的策略
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);
}
siftUp方法
添加对象的时候会调用siftUp去排序 ,这里的排序并不是真正意义上的排序,核心在(k - 1) >>> 1这个上面,这个熟悉 二叉树的人可能比较清楚,(k - 1) >>> 1是获取自己的父节点。其实这里并不是真正有序,只是保证了自己的父亲节点比自己小或者大,所以最大或者最小的值一定在最前面。
private void siftUp(int k, E x) {
//判断是否有自定义的comparator,如果有的话就直接使用comparator去排序
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}
@SuppressWarnings("unchecked")
private void siftUpComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
//每次-1取一半去对比,并不是排序,所以这里并不是真正意义上的排序
int parent = (k - 1) >>> 1;
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) {
//无符号左移1位
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;
}
这里我添加了几个数到了集合队列里面,放入前:{3,6,52,14,12,1,34,3,2,22}
,放入后:{1, 2, 3, 3, 6, 34, 14, 12, 52, 22}
,可以看到。
3 出队列
一般来说最大或者最小的数据放在队列第一个,所以只需要取下标为0的即可,然后调用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;
}
调整数组
数组这里主要是做一个调整,重新把顺序进行排序,按照之前的自定义排序,首先先拿到当前队列顶点的对象,然后拿到自己的左右及诶但
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
@SuppressWarnings("unchecked")
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;
//判断左右节点的权重,如果c权重比右节点大,那么就使用右节点
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)
c = queue[child = right];
//判断如果X小于C那么就停止了
if (comparator.compare(x, (E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = x;
}
调整大概如下,可以看到2的位置变化了,3的位置也变化了,基本都是一个个子节点
[1, 2, 8, 3, 6, 34, 14, 12, 52, 22]
1
[2, 3, 8, 12, 6, 34, 14, 22, 52]