• 【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     else21         // 使用的数组形式存储的“二叉堆”数据,上浮
    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

  • 相关阅读:
    CSS的选择符
    小例子分析C#继承机制
    socket服务端(.net)代码
    分页代码
    web.config的数据库连接字符串写法与取法
    怎么提高网站速度,对于大访问量网站如何对网站进行优化
    方维分享系统二次开发, 给评论、主题、回复、活动 加审核的功能
    方维分享系统修改,本地安装失败,后台无法登陆
    方维分享系统修改,后台一键更新缓存
    方维分享系统 第三方登录appkey申请
  • 原文地址:https://www.cnblogs.com/h--d/p/14596772.html
Copyright © 2020-2023  润新知