【Java】PriorityQueue 的实现原理

一、PriorityQueue介绍

  PriorityQueue 是基于优先级堆的无限优先级queue 。 优先级队列的元素根据它们的有序natural ordering ,或由一个Comparator在队列构造的时候提供,这取决于所使用的构造方法。 优先队列不允许null元素。 依靠自然排序的优先级队列也不允许插入不可比较的对象(这样做可能导致ClassCastException )。

  该队列的头部是相对于指定顺序的最小元素(即最小堆数据结构)。 如果多个元素被绑定到最小值,那么头就是这些元素之一 - 关系被任意破坏。 队列检索操作poll , remove , peekelement访问在队列的头部的元件。

  优先级队列是无限制的,但是具有管理用于在队列上存储元素的数组的大小的内部容量 。 它始终至少与队列大小一样大。 当元素被添加到优先级队列中时,其容量会自动增长。 没有规定增长政策的细节。

二、属性

 1 // 默认队列容量
 2 private static final int DEFAULT_INITIAL_CAPACITY = 11;
 3 
 4 // 数据数组
 5 transient Object[] queue;
 6 
 7 // 队列大小
 8 private int size = 0;
 9 
10 // 比较器
11 private final Comparator<? super E> comparator;
12 
13 // 修改次数
14 transient int modCount = 0;

三、方法

1、构造方法

 1 // 创建默认优先级队列,队列默认容量为11,其中数据数组未创建
 2 public PriorityQueue() {
 3     this(DEFAULT_INITIAL_CAPACITY, null);
 4 }
 5 
 6 // 根据初始值容量创建优先级队列
 7 public PriorityQueue(int initialCapacity) {
 8     this(initialCapacity, null);
 9 }
10 
11 // 根据创建比较器,默认容量为11优先级队列
12 public PriorityQueue(Comparator<? super E> comparator) {
13     this(DEFAULT_INITIAL_CAPACITY, comparator);
14 }
15 
16 // 根据初始值容量,比较器,创建队列
17 public PriorityQueue(int initialCapacity,
18                      Comparator<? super E> comparator) {
19     // Note: This restriction of at least one is not actually needed,
20     // but continues for 1.5 compatibility
21     if (initialCapacity < 1)
22         throw new IllegalArgumentException();
23     this.queue = new Object[initialCapacity];
24     this.comparator = comparator;
25 }
26 
27 // 根据集合创建优先级队列
28 @SuppressWarnings("unchecked")
29 public PriorityQueue(Collection<? extends E> c) {
30     if (c instanceof SortedSet<?>) {
31         SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
32         this.comparator = (Comparator<? super E>) ss.comparator();
33         initElementsFromCollection(ss);
34     }
35     else if (c instanceof PriorityQueue<?>) {
36         PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
37         this.comparator = (Comparator<? super E>) pq.comparator();
38         initFromPriorityQueue(pq);
39     }
40     else {
41         this.comparator = null;
42         initFromCollection(c);
43     }
44 }
45 
46 // 根据原优先级队列创建队列
47 @SuppressWarnings("unchecked")
48 public PriorityQueue(PriorityQueue<? extends E> c) {
49     this.comparator = (Comparator<? super E>) c.comparator();
50     initFromPriorityQueue(c);
51 }
52 
53 // 根据SortSet集合创建优先级队列
54 @SuppressWarnings("unchecked")
55 public PriorityQueue(SortedSet<? extends E> c) {
56     this.comparator = (Comparator<? super E>) c.comparator();
57     initElementsFromCollection(c);
58 }

2、add() 和 offer() 方法

 1 // 添加元素到队列中 
 2 public boolean add(E e) {
 3     return offer(e);
 4 }
 5 
 6 // 放入元素到队列中
 7 public boolean offer(E e) {
 8     if (e == null)
 9         throw new NullPointerException();
10     modCount++;
11     int i = size;
12     // 队列大小 大于等于 队列容量时
13     if (i >= queue.length)
14         // 对队列进行扩容
15         grow(i + 1);
16     size = i + 1;
17     if (i == 0)
18         // 队列中没有元素
19         queue[0] = e;
20     else)
21         // 使用的数组形式存储的“二叉堆”数据,上浮
22         // 二叉堆可自己百度
23         // 保持数据并上浮
24         siftUp(i, e);
25     return true;
26 }

  注:不明白二叉堆-最小堆的可百度,即可理解原理

3、grow() 方法

 1 // 扩容
 2 private void grow(int minCapacity) {
 3     int oldCapacity = queue.length;
 4     // Double size if small; else grow by 50%
 5     // 1、原容量小于 64,新容量为原容量的2倍+2
 6     // 2、原容量不小于 64,新容量为原容量的1.5倍
 7     int newCapacity = oldCapacity + ((oldCapacity < 64) ?
 8                                      (oldCapacity + 2) :
 9                                      (oldCapacity >> 1));
10     // overflow-conscious code
11     if (newCapacity - MAX_ARRAY_SIZE > 0)
12         // 新容量大于数组最大长度时,新容量为:Integer.MAX_VALUE,否则为:MAX_ARRAY_SIZE
13         newCapacity = hugeCapacity(minCapacity);
14     // 复制数据到新数组
15     queue = Arrays.copyOf(queue, newCapacity);
16 }

4、siftUp() 方法

 1 // 筛选 siftUp
 2 private void siftUp(int k, E x) {
 3     if (comparator != null)
 4         siftUpUsingComparator(k, x);
 5     else
 6         siftUpComparable(k, x);
 7 }
 8 
 9 // 使用选择器上浮
10 @SuppressWarnings("unchecked")
11 private void siftUpComparable(int k, E x) {
12     Comparable<? super E> key = (Comparable<? super E>) x;
13     while (k > 0) {
14         int parent = (k - 1) >>> 1;
15         Object e = queue[parent];
16         if (key.compareTo((E) e) >= 0)
17             break;
18         queue[k] = e;
19         k = parent;
20     }
21     queue[k] = key;
22 }
23 
24 // 使用选择器上浮
25 @SuppressWarnings("unchecked")
26 private void siftUpUsingComparator(int k, E x) {
27     while (k > 0) {
28         // 使用的
29         int parent = (k - 1) >>> 1;
30         Object e = queue[parent];
31         if (comparator.compare(x, (E) e) >= 0)
32             break;
33         queue[k] = e;
34         k = parent;
35     }
36     queue[k] = x;
37 }

5、poll() 方法

 1 public E poll() {
 2     if (size == 0)
 3         return null;
 4     int s = --size;
 5     modCount++;
 6     // 因为数据时最小堆,所以queue[0]最小
 7     E result = (E) queue[0];
 8     E x = (E) queue[s];
 9     queue[s] = null;
10     if (s != 0)
11         // 二叉堆存储
12         // 下浮
13         siftDown(0, x);
14     return result;
15 }

6、siftDown() 方法

 1 private void siftDown(int k, E x) {
 2         if (comparator != null)
 3             siftDownUsingComparator(k, x);
 4         else
 5             siftDownComparable(k, x);
 6     }
 7 
 8     @SuppressWarnings("unchecked")
 9     private void siftDownComparable(int k, E x) {
10         Comparable<? super E> key = (Comparable<? super E>)x;
11         int half = size >>> 1;        // loop while a non-leaf
12         while (k < half) {
13             int child = (k << 1) + 1; // assume left child is least
14             Object c = queue[child];
15             int right = child + 1;
16             if (right < size &&
17                 ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
18                 c = queue[child = right];
19             if (key.compareTo((E) c) <= 0)
20                 break;
21             queue[k] = c;
22             k = child;
23         }
24         queue[k] = key;
25     }
26 
27     @SuppressWarnings("unchecked")
28     private void siftDownUsingComparator(int k, E x) {
29         int half = size >>> 1;
30         while (k < half) {
31             int child = (k << 1) + 1;
32             Object c = queue[child];
33             int right = child + 1;
34             if (right < size &&
35                 comparator.compare((E) c, (E) queue[right]) > 0)
36                 c = queue[child = right];
37             if (comparator.compare(x, (E) c) <= 0)
38                 break;
39             queue[k] = c;
40             k = child;
41         }
42         queue[k] = x;
43     }

四、总结

  1、PriorityQueue 采用数组来存储数据的,数据结构是最小堆

  2、PriorityQueue 扩容时

    - 原容量小于 64,新容量为原容量的2倍+2

    - 原容量不小于 64,新容量为原容量的1.5倍

  3、放入元素,需要使用siftUp() 上浮方法,保证最小堆结构

  4、取出元素,需要使用downtUp() 下浮方法,保证最小堆结构

  5、PriorityQueue 非线程安全的,线程安全可以使用 PriorityBlockingQueue

上一篇:为什么会有Comparable与Comparator接口? 引入策略模式


下一篇:Java 比较器 Comparable、Comparator