PriorityQueue源码分析

Priorityqueue是Java7后加入的优先级队列,继承自AbstractQueue,而AbstractQueue,实现了Queue接口,也就是说Priorityqueue是实现了Queue接口的,带有优先级的队列。

内部实现上采用的是最小堆,即每次取出优先级最小的元素。相对来说实现上比较简单,下面简单分析一下。

一、基本属性与构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
public class PriorityQueue<E> extends AbstractQueue<E>
implements java.io.Serializable {

private static final long serialVersionUID = -7720805057305804111L;

private static final int DEFAULT_INITIAL_CAPACITY = 11;

transient Object[] queue; // non-private to simplify nested class access

private int size = 0;

private final Comparator<? super E> comparator;

transient int modCount = 0; // non-private to simplify nested class access

public PriorityQueue() {//1
this(DEFAULT_INITIAL_CAPACITY, null);
}

public PriorityQueue(int initialCapacity) {//2
this(initialCapacity, null);
}

public PriorityQueue(Comparator<? super E> comparator) {//3
this(DEFAULT_INITIAL_CAPACITY, comparator);
}

public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {//4
// 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;
}

@SuppressWarnings("unchecked")
public PriorityQueue(Collection<? extends E> c) {//5
if (c instanceof SortedSet<?>) {
SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
this.comparator = (Comparator<? super E>) ss.comparator();
initElementsFromCollection(ss);
}
else if (c instanceof PriorityQueue<?>) {
PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
this.comparator = (Comparator<? super E>) pq.comparator();
initFromPriorityQueue(pq);
}
else {
this.comparator = null;
initFromCollection(c);
}
}

@SuppressWarnings("unchecked")
public PriorityQueue(PriorityQueue<? extends E> c) {//6
this.comparator = (Comparator<? super E>) c.comparator();
initFromPriorityQueue(c);
}

@SuppressWarnings("unchecked")
public PriorityQueue(SortedSet<? extends E> c) {//7
this.comparator = (Comparator<? super E>) c.comparator();
initElementsFromCollection(c);
}

private void initFromPriorityQueue(PriorityQueue<? extends E> c) {
if (c.getClass() == PriorityQueue.class) {
this.queue = c.toArray();
this.size = c.size();
} else {
initFromCollection(c);
}
}

private void initElementsFromCollection(Collection<? extends E> c) {
Object[] a = c.toArray();
// If c.toArray incorrectly doesn't return Object[], copy it.
if (a.getClass() != Object[].class)
a = Arrays.copyOf(a, a.length, Object[].class);
int len = a.length;
if (len == 1 || this.comparator != null)
for (int i = 0; i < len; i++)
if (a[i] == null)
throw new NullPointerException();
this.queue = a;
this.size = a.length;
}

private void initFromCollection(Collection<? extends E> c) {
initElementsFromCollection(c);
heapify();
}

}

1、私有属性

  • DEFAULT_INITIAL_CAPACITY:静态常量,也就是默认的初始化容量,11
  • transient Object[] queue:存储元素的数组,Object类型的
  • private int size:表示元素数量
  • comparator:比较器,用于优先级的比较,默认情况下是最小堆
  • transient int modCount:修改的次数,用于fast-fail

2、构造函数

  1. 前三个构造器实际上都是调用第四个构造器,即制定初始化容量与比较器,而构造器4也很简单,就是根据给定的初始容量和构造器初始化数组和绑定比较器
  2. 后面的三个构造器用于指定一个已有的集合初始化,根据输入类型的不同,分为以下几种:
    • Collection:再继续判断:
      • 如果有序的如下面的PriorityQueue或者SortedSet,执行下面对应的方法
      • 如果是无序的,则比较器为null,调用initFromCollection方法
    • PriorityQueue: 绑定原有的比较器,然后调用initFromPriorityQueue方法
    • SortedSet:绑定原有的比较器,然后调用initElementsFromCollection方法

3、三个init相关的方法

上面根据指定集合类型的不同,调用了不同的初始化方法:

  • initElementsFromCollection:这个方法的作用就是将原有集合的数组复制过来,因此适用于SortedSet,因为SortedSet中的元素已经有序了

  • initFromCollection:这个方法适用于指定集合无序的情况,先调用上面的initElementsFromCollection复制数组,然后再调用heapify方法调整。

  • initFromPriorityQueue:确定指定集合是PriorityQueue,说明原数组已经有序,则直接指定Object数组与size到原PriorityQueue上。如果不是PriorityQueue,则调用下面的initFromCollection

    与上面两个不同的是,这个方法并没有复制数组而只是单纯的指针指向。

二、关键方法

1、扩容

当容量不够是时候,Object数组就需要扩容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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 static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

扩容的原则注释写的很明白,如果数组小,就扩大一倍,不然就扩大一半

2、add与offer方法

向队列中增加一个元素,可以调用add或者offer方法,其实在实现上都是一样的,都是调用了offer方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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;
}

获取当前数组大小,如果不够了就扩容,然后再i位置放入元素,调用siftUp方法来维护堆的性质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}

迭代的方式,不断上滤

3、poll与heapify方法

对offer对应的就是poll方法了

1
2
3
4
5
6
7
8
9
10
11
12
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方法下滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
}

前面还有一个heapify,可以发现这就是一个完整的筛选法建堆的过程

1
2
3
4
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}

4、remove方法

删除是把最后一个元素放到指定位置,然后执行上滤或者下滤,两者只会执行其中的一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public boolean remove(Object o) {
int i = indexOf(o);
if (i == -1)
return false;
else {
removeAt(i);
return true;
}
}

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);
if (queue[i] == moved) {
siftUp(i, moved);
if (queue[i] != moved)
return moved;
}
}
return null;
}

5、其他方法

其他的方法比如clear、size、contains等实现都与ArrayList相类似就不一一介绍,PriorityQueue的核心就是其内部实现的堆,以及堆性质的维护,包括下滤与筛选法建堆,迭代法上滤,还有删除的时候选择上滤或者下滤中的一种。另外就是比较器,默认情况下是一个最小堆,如果需要实现最大堆或者持有其他类型的元素,则需要在构造函数中输入一个自定义的Comparator。

三、参考地址

http://blog.csdn.net/hxpjava1/article/details/21478323