一、PriorityQueue介绍
PriorityQueue 是基于优先级堆的无限优先级queue 。 优先级队列的元素根据它们的有序natural ordering ,或由一个Comparator
在队列构造的时候提供,这取决于所使用的构造方法。 优先队列不允许null
元素。 依靠自然排序的优先级队列也不允许插入不可比较的对象(这样做可能导致ClassCastException
)。
该队列的头部是相对于指定顺序的最小元素(即最小堆数据结构)。 如果多个元素被绑定到最小值,那么头就是这些元素之一 - 关系被任意破坏。 队列检索操作poll
, remove
, peek
和element
访问在队列的头部的元件。
优先级队列是无限制的,但是具有管理用于在队列上存储元素的数组的大小的内部容量 。 它始终至少与队列大小一样大。 当元素被添加到优先级队列中时,其容量会自动增长。 没有规定增长政策的细节。
二、属性
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 /** 2 * 优先级队列 3 * 数据存储使用的是数组存,结构是最小堆,即顶上元素最小 4 */ 5 public class TestPriorityQueue { 6 7 public static void main(String[] args) { 8 9 PriorityQueue<Integer> queue = new PriorityQueue<>(10, (Integer::compare)); 10 queue.add(1); 11 queue.add(7); 12 queue.add(8); 13 queue.add(2); 14 queue.add(3); 15 queue.add(9); 16 queue.add(5); 17 queue.add(4); 18 queue.add(6); 19 20 System.out.println(queue); 21 for (int i = 0; i < 5; i++) { 22 System.out.println(queue.poll()); 23 } 24 System.out.println(queue); 25 26 } 27 }
五、总结
1、PriorityQueue 采用数组来存储数据的,数据结构是最小堆
2、PriorityQueue 扩容时
- 原容量小于 64,新容量为原容量的2倍+2
- 原容量不小于 64,新容量为原容量的1.5倍
3、放入元素,需要使用siftUp() 上浮方法,保证最小堆结构
4、取出元素,需要使用downtUp() 下浮方法,保证最小堆结构
5、PriorityQueue 非线程安全的,线程安全可以使用 PriorityBlockingQueue