PriorityQueue 源码分析

Posted zhuxudong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PriorityQueue 源码分析相关的知识,希望对你有一定的参考价值。

PriorityQueue

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

创建实例

    /**
     * 默认初始容量
     */
    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;
    }

以上是关于PriorityQueue 源码分析的主要内容,如果未能解决你的问题,请参考以下文章

PriorityQueue源码分析

PriorityQueue原理分析——基于源码

死磕 java集合之PriorityQueue源码分析

PriorityQueue源码解析

# Java 常用代码片段

# Java 常用代码片段