zoukankan      html  css  js  c++  java
  • PriorityQueue 源码分析

    PriorityQueue

    PriorityQueue 能解决什么问题?什么时候使用 PriorityQueue?

    1)PriorityQueue 基于优先级堆实现的无界优先级队列,优先级队列的元素根据自然顺序或指定的比较器进行排序。
    当需要按照指定的优先级读取元素时,可以使用 PriorityQueue。
    2)优先级队列不允许使用 null 元素,并且元素必须实现 Comparable 接口。
    3)PriorityQueue 的头元素是排序最小的元素,存在多个最小的头元素时,随机选取其中的一个。
    4)PriorityQueue 不是线程安全的,如果需要并发访问,请使用 PriorityBlockingQueue。
    

    如何使用 PriorityQueue?

    1)需要按照优先级读取元素的场景,如延时任务处理。
    2)PriorityQueue 可以解决 topN 问题(由于键不能重复,使用 TreeMap 时需要维护指定键出现的次数)。
    

    使用 x 有什么风险?

    1)PriorityQueue 未限制存储容量,可能导致内存溢出。
    

    x 核心操作的实现原理?

    • 创建实例
        /**
         * 默认初始容量
         */
        private static final int DEFAULT_INITIAL_CAPACITY = 11;
    
        /**
         * 用于构建平衡二叉堆的底层数组
         * 父节点:queue[n]
         * 左子节点:queue[2*n+1]
         * 右子节点:queue[2*(n+1)]
         * 计算父节点:(n-1)/2
         */
        transient Object[] queue; // non-private to simplify nested class access
    
        /**
         * 优先级队列中的元素个数
         */
        int size;
    
        /**
         * 排序元素的比较器,如果为 null,则根据元素的自然顺序排序
         */
        private final Comparator<? super E> comparator;
    
        /**
         * PriorityQueue 被结构化修改的次数,用于实现 Fast-Fail
         */
        transient int modCount;     // non-private to simplify nested class access
    
        /**
         * 创建一个初始容量为 11,
         * 队列元素根据自然顺序排序的空 PriorityQueue
         */
        public PriorityQueue() {
            this(DEFAULT_INITIAL_CAPACITY, null);
        }
    
        /**
         * 创建一个初始容量为 initialCapacity,
         * 队列元素根据自然顺序排序的空 PriorityQueue
         */
        public PriorityQueue(int initialCapacity) {
            this(initialCapacity, null);
        }
    
        /**
         * 创建一个初始容量为 11,
         * 队列元素根据指定比较器 comparator 排序的空 PriorityQueue
         */
        public PriorityQueue(Comparator<? super E> comparator) {
            this(DEFAULT_INITIAL_CAPACITY, comparator);
        }
    
        /**
         * 创建一个初始容量为 initialCapacity,
         * 队列元素根据指定比较器 comparator 排序的空 PriorityQueue
         */
        public PriorityQueue(int initialCapacity,
                Comparator<? super E> comparator) {
            // 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;
        }
    
    • 往优先级队列中添加元素:add、offer
        /**
         * 往优先级队列中插入元素
         */
        @Override
        public boolean add(E e) {
            return offer(e);
        }
    
        /**
         * 插入元素到 PriorityQueue 中
         */
        @Override
        public boolean offer(E e) {
            if (e == null) {
                throw new NullPointerException();
            }
            modCount++;
            // 读取元素个数
            final int i = size;
            // 元素个数超出队列容量
            if (i >= queue.length) {
                // 则进行扩容
                grow(i + 1);
            }
            // 插入元素并平衡二叉堆结构
            siftUp(i, e);
            // 递增容量
            size = i + 1;
            return true;
        }
    
        /**
         * The maximum size of array to allocate.
         */
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
        private void grow(int minCapacity) {
            // 读取旧容量
            final int oldCapacity = queue.length;
            /**
             * 1)旧容量 < 64,执行【双倍+2】扩容
             * 2)执行 1.5 倍向下取整扩容
             */
            int newCapacity = oldCapacity + (oldCapacity < 64 ?
                    oldCapacity + 2 :
                        oldCapacity >> 1);
            // 新容量超出 Integer.MAX_VALUE - 8
            if (newCapacity - MAX_ARRAY_SIZE > 0) {
                newCapacity = PriorityQueue.hugeCapacity(minCapacity);
            }
            // 拷贝源数组
            queue = Arrays.copyOf(queue, newCapacity);
        }
    
        private static int hugeCapacity(int minCapacity) {
            // 容量溢出
            if (minCapacity < 0) {
                throw new OutOfMemoryError();
            }
            /**
             *  1)minCapacity = MAX_ARRAY_SIZE,则返回 MAX_ARRAY_SIZE
             *  2)否则返回 Integer.MAX_VALUE
             */
            return minCapacity > MAX_ARRAY_SIZE ?
                    Integer.MAX_VALUE :
                        MAX_ARRAY_SIZE;
        }
    
        /**
         * 在索引 k 处插入元素 x,通过向上提升 x 直到它大于等于它的父节点或直到根节点来维持堆不变。
         */
        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) {
            final Comparable<? super E> key = (Comparable<? super E>) x;
            // 索引为 0 时,当前元素就是根
            while (k > 0) {
                // 计算父节点索引
                final int parent = k - 1 >>> 1;
                // 读取父元素值
                final Object e = queue[parent];
                // 当前元素大于等于父元素,则不需要额外的操作来维持二叉堆结构,可以直接退出
                if (key.compareTo((E) e) >= 0) {
                    break;
                }
                // 将父元素下移
                queue[k] = e;
                // 递归处理祖父节点
                k = parent;
            }
            // 将当前元素插入到目标位置
            queue[k] = key;
        }
    
        @SuppressWarnings("unchecked")
        private void siftUpUsingComparator(int k, E x) {
            while (k > 0) {
                final int parent = k - 1 >>> 1;
                final Object e = queue[parent];
                if (comparator.compare(x, (E) e) >= 0) {
                    break;
                }
                queue[k] = e;
                k = parent;
            }
            queue[k] = x;
        }
    
    • 查看堆顶元素
        /**
         * 读取堆顶元素,最小值
         */
        @Override
        @SuppressWarnings("unchecked")
        public E peek() {
            /**
             * 1)优先级队列为空,则防护 null
             * 2)返回索引为 0 的堆顶元素
             */
            return size == 0 ? null : (E) queue[0];
        }
    
    • 读取并移除堆顶元素
        /**
         * 读取并移除堆顶元素
         */
        @Override
        @SuppressWarnings("unchecked")
        public E poll() {
            if (size == 0) {
                return null;
            }
            final int s = --size;
            modCount++;
            final E result = (E) queue[0];
            final E x = (E) queue[s];
            queue[s] = null;
            if (s != 0) {
                siftDown(0, x);
            }
            return result;
        }
    
        /**
         * 在索引 k 处插入元素 x,通过向下移动 x 直到它小于等于它的子节点或它本身就是子节点
         * 为止,来维持堆不变。
         */
        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) {
            final Comparable<? super E> key = (Comparable<? super E>)x;
            // 计算二分节点索引
            final int half = size >>> 1; // loop while a non-leaf
            // 目标索引在二分节点前【如果 k >= half 则表示 k 在数组中没有子节点,无需处理】
            while (k < half) {
                // 计算左子节点索引
                int child = (k << 1) + 1; // assume left child is least
                // 读取左子节点值
                Object c = queue[child];
                // 计算右子节点索引
                final int right = child + 1;
                // 读取左、右子节点的最小值
                if (right < size &&
                        ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0) {
                    // 左子节点值 > 右子节点值,则读取右子节点值
                    c = queue[child = right];
                }
                // 目标值小于等于 c,则已经是升序排序,直接退出
                if (key.compareTo((E) c) <= 0) {
                    break;
                }
                /**
                 * 目标值大于 c,将最小的子节点值放在父节点上
                 */
                queue[k] = c;
                // 更新索引为值小的子节点索引
                k = child;
            }
            // 插入目标值
            queue[k] = key;
        }
    
        @SuppressWarnings("unchecked")
        private void siftDownUsingComparator(int k, E x) {
            final int half = size >>> 1;
                while (k < half) {
                    int child = (k << 1) + 1;
                    Object c = queue[child];
                    final int right = child + 1;
                    if (right < size &&
                            comparator.compare((E) c, (E) queue[right]) > 0) {
                        c = queue[child = right];
                    }
                    if (comparator.compare(x, (E) c) <= 0) {
                        break;
                    }
                    queue[k] = c;
                    k = child;
                }
                queue[k] = x;
        }
    
    • 清空优先级队列
        /**
         * 移除优先级队列中的所有元素
         */
        @Override
        public void clear() {
            modCount++;
            for (int i = 0; i < size; i++) {
                queue[i] = null;
            }
            size = 0;
        }
    
    • 读取元素个数
        @Override
        public int size() {
            return size;
        }
    
  • 相关阅读:
    游吟诗人阿严
    学霸女
    sql group by 分组后查询最新的一条数据
    腐朽
    我喜欢不说话的山川
    redis启动
    php 时间轴,数据统计(最近7天的数据)
    php options 请求跨域
    mac关机声音
    JVM-内存模型
  • 原文地址:https://www.cnblogs.com/zhuxudong/p/10015316.html
Copyright © 2011-2022 走看看