前言
前言点击此处查看:
http://blog.csdn.net/wang7807564/article/details/79113195
PriorityQueue
PriorityQueue是基于堆数据结构的一个无界队列,这个优先队列中的元素可以默认自然排序或者通过提供的Comparator(比较器)在队列实例化的时排序。优先队列不允许加入空值(null),或者其他不可比较的对象,并且PriorityQueue内的元素不能是基本数据类型,必须是一个类。优先队列会使用Java的Comparable和Comparator接口对队内的元素进行比较,优先队列的头是基于自然排序或者Comparator排序后的最小元素。当多个元素具有相同的优先级时,在取出头元素的时候,可能会随机取出一个。
值得指出的是,PriorityQueue是非线程安全的,如果需要使用线程安全的队列,则使用并发包中为我们提供的PriorityBlockingQueue,它实现BlockingQueue接口。
PriorityQueue的默认内部实现了一个小顶堆,使用Object类型的数组存储数据,当每次数据插入的时候,判断容量是否足够,如果不足够,自动扩容。
例如下面这段代码:
public static void main(String[] args){
Queue<Integer> q = new PriorityQueue<>();
int[] arr = {1,8,3,2,4,6,7,5};
for(int i:arr){
q.add(i);
}
System.out.println(q);
Integer ele = null;
while ((ele = q.poll()) != null){
System.out.print(ele + " ");
}
}
输出的结果是:
[1, 2, 3, 5, 4, 6, 7, 8]
1 2 3 4 5 6 7 8
可以看到,直接输出队列中的存储数据和使用poll()方法逐个获取队头元素打印出来输出元素的顺序是不同的,使用poll()方法获取的顺序是有序的,而直接打印输出获取的顺序是内部实现的小顶堆按照宽度有限打印出来的结果。其在内部的存储是这样的:
堆是一个平衡树,对于小顶堆来说,满足任何一个父节点比其左右子节点都要小的特点。而大顶堆则反过来。这里按照宽度优先输出的顺序就是:
[1, 2, 3, 5, 4, 6, 7, 8]
而在使用poll()取出元素的时候,实现堆排序。堆排序的过程是:
1. 建立一个堆(此时已经完成)
2. 取出堆顶元素,对于小顶堆来讲,堆顶元素一定是最小的
3. 将最后一个元素(此时也就是8)放到堆顶的位置
4. 从上到下依次调整这棵平衡树,使其满足堆的特点
5. 在调整的时候注意,如果在小顶堆中,某个节点的左右孩子节点都比该节点小,则在交换时,交换两个孩子节点中最小的那个
6. 那么,在取出堆顶元素1之后,整个堆的结构就变成了:
[2, 4, 3, 5, 8, 6, 7]
用图示来表示就是:
其实现的核心代码是:
1. 添加元素:
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;
// 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 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;
}
- 向下调整:
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;
}
- 取出元素
public E pollFirst() {
int h = head;
@SuppressWarnings("unchecked")
E result = (E) elements[h];
// Element is null if deque empty
if (result == null)
return null;
elements[h] = null; // Must null out slot
head = (h + 1) & (elements.length - 1);
return result;
}
public E pollLast() {
int t = (tail - 1) & (elements.length - 1);
@SuppressWarnings("unchecked")
E result = (E) elements[t];
if (result == null)
return null;
elements[t] = null;
tail = t;
return result;
}
由于默认实现的是小顶堆,可以通过传入比较器来实现大顶堆:
PriorityQueue<Integer> q=new PriorityQueue<Integer>(10, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});