1类签名与简介

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

PriorityQueue是一个基于优先级堆的无限队列,无限的意思是说队列的容量可以自动扩容,最大容量为整形最大值。扩容规定如下当容量小于64时扩大为原来的2倍,否则扩大为原来的1.5倍(也就是容量增大50%)。

优先级队列不允许null元素,也不允许插入不可比较的对象。不可比较是指待插入对象的类没有实现Comparable接口,或者构造PriorityQueue对象是没有提供一个待插入对象的comparator比较器。强行插入不可比较对象会报ClassCastException异常。举个例子,现有一个Student类,只有以下两种情况才能将Student的对象放入PriorityQueue中:

(1)Student实现Comparable接口

(2)new PriorityQueue时提供一个Student的比较器:Queue<Student> students = new PriorityQueue<>(new MyComparator())

该队列的头部是相对于指定顺序的最小元素。 如果多个元素被绑定到最小值,那么头就是这些元素之一。

优先级堆(小顶堆)是用数组实现的,对于小顶堆的性质请执行查阅相关数据结构。入队出队的方法时间复杂度都是O(logn)。

请注意,此实现不同步。若想要在多线程并发,请使用线程安全的PriorityBlockingQueue类。

2 入队

public boolean add(E e) {
return offer(e);
} public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
if (i >= queue.length)
grow(i + 1);
size = i + 1;
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}

add和offer都是入队实现,add其实就是调用offer方法实现的,所以两者本质上没有区别。

offer首先会判断插入元素是否为null,优先队列不允许插入null的元素。接下来判断队列是否满了,满了就扩容。如果插入的是队列第一个元素就直接放在数组0号位。否则就进行上移调整siftUp。上移调整是从堆的最后一个节点(也就是队尾)开始不断通过比较parent节点找到插入的位置。

private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
} //实现Comparable接口的情况
private void siftUpComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = key;
} //提供比较器Comparator的情况
private void siftUpUsingComparator(int k, E x) {
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (comparator.compare(x, (E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = x;
}

每次插入都进行siftUp保证了始终是一个小顶堆,不会因为新的节点破坏堆的结构。

siftUpComparable和siftUpUsingComparator过程是一样的,唯一不同的是不同的接口调用的不同比较方法。

3出队

出队是移除队列队头的元素,即数组的0号位存储的元素,也是堆结构的堆顶。

public E poll() {
if (size == 0)
return null;
int s = --size;
modCount++;
E result = (E) queue[0];
E x = (E) queue[s];
queue[s] = null;
if (s != 0)
siftDown(0, x);
return result;
}

poll原理如下,把最后一个元素放到堆顶(数组0号位),然后从堆顶开始往下调整siftDown,直到满足最小堆为止。siftDown实现过程如下

private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
} //内部排序接口调用siftDownComparable
private void siftDownComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>)x;
int half = size >>> 1; // loop while a non-leaf
while (k < half) {
int child = (k << 1) + 1; // assume left child is least
Object c = queue[child];
int right = child + 1;
if (right < size &&
((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
c = queue[child = right];
if (key.compareTo((E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = key;
} //有外部比较器调用siftDownUsingComparator
private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
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;
}

4总结

关于本篇文章,个人感觉描述的不是很清楚。

因为觉得不适合花费其他篇幅去详细介绍“Comparator与Comparable”以及堆排序等相关知识。如果觉得有问题的话建议先了解一下这些相关的基础知识。

Java源码阅读PriorityQueue的更多相关文章

  1. Java源码阅读的真实体会(一种学习思路)

    Java源码阅读的真实体会(一种学习思路) 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈 ...

  2. Java源码阅读的真实体会(一种学习思路)【转】

    Java源码阅读的真实体会(一种学习思路)   刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+ ...

  3. 如何阅读Java源码 阅读java的真实体会

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心.   说到技术基础,我打个比 ...

  4. [收藏] Java源码阅读的真实体会

    收藏自http://www.iteye.com/topic/1113732 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我 ...

  5. java源码阅读Hashtable

    1类签名与注释 public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, C ...

  6. Java源码阅读Stack

    Stack(栈)实现了一个后进先出(LIFO)的数据结构.该类继承了Vector类,是通过调用父类Vector的方法实现基本操作的. Stack共有以下五个操作: put:将元素压入栈顶. pop:弹 ...

  7. Java源码阅读顺序

    阅读顺序参考链接:https://blog.csdn.net/qq_21033663/article/details/79571506 阅读源码:JDK 8 计划阅读的package: 1.java. ...

  8. java源码阅读LinkedBlockingQueue

    1类签名与简介 public class LinkedBlockingQueue<E> extends AbstractQueue<E> implements Blocking ...

  9. java源码阅读ArrayBlockingQueue

    1类签名与简介 public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQ ...

随机推荐

  1. Linux MMC介绍

    1. 介绍 Linux中,将包括MMC.SD.SDIO统称为MMC子系统 MMC子系统从功能上可分为三个层次 - card层: Card驱动, 或称client驱动 - core层: MMC的核心层, ...

  2. 51nod 几道题

    1001 数组中和等于K的数对 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 给出一个整数K和一个无序数组A,A的元素为N个互不相同的整数, 找出数组A中所有和等于 ...

  3. 细说robots.txt

    robots.txt Robots协议(也称为爬虫协议.机器人协议等)的全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站通过Robots协议告诉搜索引擎哪些页面可 ...

  4. 为用户分配角色 C#

    开发网站时,在后台管理系统中,如果有多类角色,将会涉及到为角色分配用户的功能,或者是为用户选择角色.为用户分配角色相对来说操作的数据量比较小,因为系统所设定的角色不会有很多种.而如果是为角色分配用户, ...

  5. 使用python抓取并分析数据—链家网(requests+BeautifulSoup)(转)

    本篇文章是使用python抓取数据的第一篇,使用requests+BeautifulSoup的方法对页面进行抓取和数据提取.通过使用requests库对链家网二手房列表页进行抓取,通过Beautifu ...

  6. AC日记——[HNOI2008]越狱 bzoj 1008

    1008 思路: 越狱情况=总情况-不越狱情况: 代码: #include <cstdio> #include <cstring> #include <iostream& ...

  7. 【转】进程、线程、 GIL全局解释器锁知识点整理

    转自:https://www.cnblogs.com/alex3714/articles/5230609.html 本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线 ...

  8. <div>之定位

    在使用盒子模型的过程中,如何放置各种类型的“盒子”,就存在定位.浮动等问题.下面就日常运用过程中出现过的情况总结如下(陆续加入中....) 一.图片直接做<div>的背景 在<div ...

  9. (4)java基础知识

    一.注释 java的注释方法主要有三种 1.单行注释 // 2.多行注释 /*  内容 */ 3.文档注释 /** * * */ 这种方法注释用于生成一份API文档,主要说明 方法的功能.参数.返回值 ...

  10. [BZOJ 2208] 连通数

    Link: BZOJ 2208 连通数 Solution: 传递闭包模板题 传递闭包是集合中最小的二元关系,其实就是对二元关系的不断拓展,一般用$floyd$求解 这里要先跑一遍$tarjan$求出$ ...