1.PriorityQueue
1.1二叉堆
用数组实现的二叉树
堆总是一棵完全二叉树。
最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。
1.2PriorityQueue
一个基于优先级的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序。插入队列的对象必须是可比较大小的.
优先队列只保证poll出来的值是最大值(或者最小值),虽然实现了 Collection 和 Iterator 接口中的所有接口方法,但是对其对象进行迭代并遍历时,不能保证有序性。如果想要实现有序遍历,建议采用 Arrays.sort(queue.toArray()) 进行处理.
没有指定Comparator:
PriorityQueue<Integer> queue = new PriorityQueue<>();
queue.add(13);
queue.add(-3);
queue.add(20);
queue.add(-25);
System.out.println(queue);
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
输出:
-25
-3
13
20
指定Comparator:
public class Student {
private String name;
private Integer score;
}
public static void main(String[] args) {
Comparator<Student> scoreComparator = new Comparator<Student>() {
public int compare(Student c1, Student c2) {
if (c1.getScore() > c2.getScore()) {
return -1;
} else if (c1.getScore() < c2.getScore()) {
return 1;
} else return 0;
}
};
PriorityQueue<Student> priorityQueue = new PriorityQueue<>(11, scoreComparator);
priorityQueue.add(new Student("A", 99));
priorityQueue.add(new Student("B", 100));
priorityQueue.add(new Student("C", 89));
priorityQueue.add(new Student("D", 98));
System.out.println(priorityQueue.poll());
System.out.println(priorityQueue.poll());
System.out.println(priorityQueue.poll());
System.out.println(priorityQueue.poll());
}
输出:
Student{name='B', score=100}
Student{name='A', score=99}
Student{name='D', score=98}
Student{name='C', score=89}
代码详解:
//构造方法中,如果不指定大小的话,默认大小为 11
private static final int DEFAULT_INITIAL_CAPACITY = 11;
//能扩容的最大值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//存放数据的数组
//the two children of queue[n] are queue[2*n+1] and queue[2*(n+1)].
transient Object[] queue;
//当前大小
private int size = 0;
// 大小比较器,如果不指定,按照自然序排序
private final Comparator<? super E> comparator;
//队列被改变的次数
transient int modCount = 0;
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);//扩容
size = i + 1;
if (i == 0)
queue[0] = e;
else
siftUp(i, e);//重新排序
return true;
}
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// 如果节点个数小于 64,那么增加的 oldCap + 2 的容量
// 如果节点数大于等于 64,那么增加 oldCap 的一半
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
//设置最大值
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);
}
@SuppressWarnings("unchecked")
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;
}
@SuppressWarnings("unchecked")
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;
}
小顶堆中,向优先队列中插入元素E时,首先获取E的父节点元素parent(queue[(E的下标位置-1)/2]),然后E和parent比较,如果parent小于E,则E的位置为队列尾;如果parent大于E,则两者的位置互换,然后E再和Parent的parent比较。
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 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;
}
@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;
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;
}
小顶堆中,删除第一个元素E=queue[0] ,首先获取E的l两个子节点L= queue[1] 和R=queue[2],然后L和R比较,选出最小值min(L,R)=c=queue[n],设置queue[0]=c;再对c原先的子节点进行比较,找出最小值,设置为queue[n]的值。
2.PriorityBlockingQueue
PriorityQueue 的线程安全版本,能自动扩容的无界队列.使用ReentrantLock+Condition控制.
//构造方法中,如果不指定大小的话,默认大小为 11
private static final int DEFAULT_INITIAL_CAPACITY = 11;
//存放数据的数组
//the two children of queue[n] are queue[2*n+1] and queue[2*(n+1)].
private transient Object[] queue;
//当前大小
private transient int size;
// 大小比较器,如果不指定,按照自然序排序
private transient Comparator<? super E> comparator;
// 并发控制所用的锁
private final ReentrantLock lock;
private final Condition notEmpty;
// 获取这个锁,才能进行扩容操作
private transient volatile int allocationSpinLock;
public void put(E e) {
offer(e); // 无边队列,不会阻塞
}
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();// 获取独占锁
int n, cap;
Object[] array;
while ((n = size) >= (cap = (array = queue).length))
tryGrow(array, cap);//扩容
try {
Comparator<? super E> cmp = comparator;
// 节点添加到二叉堆中
if (cmp == null)
siftUpComparable(n, e, array);
else
siftUpUsingComparator(n, e, array, cmp);
size = n + 1;
notEmpty.signal();// 唤醒等待的读线程
} finally {
lock.unlock();
}
return true;
}
private void tryGrow(Object[] array, int oldCap) {
lock.unlock(); //释放独占锁,扩容操作和读操作可以同时进行,提高吞吐量。
Object[] newArray = null;
// CAS 操作,获取锁
if (allocationSpinLock == 0 &&
UNSAFE.compareAndSwapInt(this, allocationSpinLockOffset,
0, 1)) {
try {
int newCap = oldCap + ((oldCap < 64) ?
(oldCap + 2) : // grow faster if small
(oldCap >> 1));
if (newCap - MAX_ARRAY_SIZE > 0) { // possible overflow
int minCap = oldCap + 1;
if (minCap < 0 || minCap > MAX_ARRAY_SIZE)
throw new OutOfMemoryError();
newCap = MAX_ARRAY_SIZE;
}
//如果 queue != array,说明在allocationSpinLock CAS之前,有其他线程给 queue 分配了其他的空间
if (newCap > oldCap && queue == array)
newArray = new Object[newCap];
} finally {
// 重置,也就是释放锁
allocationSpinLock = 0;
}
}
if (newArray == null) // 其他的线程也在做扩容的操作
Thread.yield();
lock.lock();// 重新获取锁
if (newArray != null && queue == array) {
queue = newArray;
System.arraycopy(array, 0, newArray, 0, oldCap);
}
}