【java容器的刻意练习】【十六】PriorityQueue的底层结构

上一篇讲到ArrayDeque作为队列,性能碾压了LinkedList。所以,我们用顺序队列的时候,优先选择ArrayDeque。

那么,今天我们继续看看另外一种队列,优先级队列PriorityQueue。

public class PriorityQueue<E> extends AbstractQueue<E>
    implements java.io.Serializable

PriorityQueue继承了AbstractQueue抽象类,并实现了Serializable接口。而AbstractQueue抽象类实现了Queue接口,对其中方法进行了一些通用的封装,如下图。

PriorityQueue继承
Queue接口
PriorityQueue接口

先看看PriorityQueue内部是什么结构?

    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }

调用了另外一个构造函数this,传入2个参数,其中一个参数是:

private static final int DEFAULT_INITIAL_CAPACITY = 11;

跳过去this构造函数看看:

    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;
    }
    /**
     * Priority queue represented as a balanced binary heap: the two
     * children of queue[n] are queue[2*n+1] and queue[2*(n+1)].  The
     * priority queue is ordered by comparator, or by the elements'
     * natural ordering, if comparator is null: For each node n in the
     * heap and each descendant d of n, n <= d.  The element with the
     * lowest value is in queue[0], assuming the queue is nonempty.
     */
    transient Object[] queue; // non-private to simplify nested class access

原来优先队列底层数据结构是个数组,而且这个queue数组是一个平衡的二叉堆。

平衡二叉堆

什么是平衡二叉堆?

  • 每个节点,叶子数只能小于等于2
  • 对于堆中的每个节点n和n的每个后代d, n <= d。
  • 非空队列,最小值元素肯定在队列第0个位置,就是根节点。
  • 队列(就是我们这里的queue数组)中某个位置n,它的的两个叶子肯定在队列2n+1和2(n+1)。
    最重点是第四点,对于任意位置,是可以通过线性公式快速算出叶子的位置的!那么,对于数组来说,这样的查找性能就是最高的!

这一堆概念初学者看到肯定是一脸懵逼的了。别方,我们来打个比方。

来看看古代打仗的矛兵阵。

  • 要求第一个是最矮的矛兵。
  • 之后每人身后要站有2名矛兵,而且都要比前面的矛兵高。
  • 形成从低到高的三角形梯队。

当将军喊列队的时候,前面的矛兵喊自己的位置报数n。那么,后面矛兵就跟根据公式计算左2n+1和右2(n+1)快速找到自己的位置并进行报数。

例如,某个矛兵喊5。那么,他身后的2名矛兵就马上喊出自己位置,左边那位排队列11,右边那位矛兵排12。

这样,军队就能快速从列队和兵阵快速变换。

平衡二叉堆储存于数组中

平衡二叉堆是怎么排序和维护的呢?不着急,我们后面会慢慢讲到。先看回构造函数最后保存的一个参数comparator

queue数组注释里面还提到一句:“如果comparator为null,则优先级队列会按照comparator或元素的自然顺序排序。”我们来看看comparator

private final Comparator<? super E> comparator;

而这个comparator是一个Comparator接口的实例,默认是空。

Comparator接口可以实现自定义排序,实现Comparator接口时,要重写compare方法:
int compare(Object o1, Object o2) 返回一个基本类型的整型

  • o1 > o2,升序
  • o1 < o2,降序

例如如下代码:

    static Comparator<Integer> cmp = new Comparator<Integer>() {
        public int compare(Integer o1, Integer o2) {
            // 升序排列
            return o1 > o2;
        }
    };

...
// 把比较器cmp传给PriorityQueue,实现升序排列
Queue priorityQueue = new PriorityQueue(cmp);

比较器也先到这里,后面我们继续详细说。

到现在,我们已经知道了:

PriorityQueue底层是个平衡二叉堆,用数组方式来储存,可以自定义比较器来实现想要的优先级排序。
PriorityDeque用的是小顶堆

大小顶堆

推荐阅读更多精彩内容

  • 一、题目简介及问题分析 原题链接:中文版、英文版 本文首发于心安-XinAnzzZ 的个人博客,转载请注明出处~ ...
    XinAnzzZ阅读 99评论 0 0
  • 本文取自工匠若水的qq群里的Java基础题目,把里面有关Java集合放在一起。全文github地址 35.Arra...
    蝉翅的空响阅读 142评论 0 0
  • package java.util包中的Collection相关接口和类如下图: 仅讨论Java.util包中的常...
    maxwellyue阅读 1,345评论 0 4
  • 1.Java容器类图 Collection:一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入...
    TomyZhang阅读 333评论 0 0
  • Java常见面试题总结 @Date 2016.06.22 Java基础知识 1.Java 中应该使用什么数据类型来...
    voltric阅读 824评论 0 2
  • alert效 表单效果 模态框效果 导航栏效果 导航栏效果 字体图标 作品库1 简历 个人网站 导航栏 模糊框
    QQQQQCY阅读 416评论 0 0
  • 又是一个樱花的季节 行人们都在赞叹 瓶里的插花 也显得可爱 我愿化作一场风 在你经过这樱花林的时刻到来 吹落轻柔的...
    班乐阅读 138评论 0 2
  • 每天可以想念你 就像是我的空气一样 是那种带着晨光 花香的空气 念你 马上见到你了 一种幸福的感觉
    H喂阅读 30评论 0 0
  • 东莞市常平镇这个地方,经济实力排在东莞前五,却也颇有些小镇风情。除了镇中心的闹市区外,其它地方人不算多。车流适中,...
    曾晨风阅读 892评论 1 1
  • 很多时候我都在想,这一开始就是个错吧,错的离谱。 “你只是抬了一下头,我就觉得天上的星星都在对你笑。”我...
    季节里开出的花阅读 224评论 0 0