Java源码阅读PriorityQueue
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的更多相关文章
- Java源码阅读的真实体会(一种学习思路)
Java源码阅读的真实体会(一种学习思路) 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈 ...
- Java源码阅读的真实体会(一种学习思路)【转】
Java源码阅读的真实体会(一种学习思路) 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+ ...
- 如何阅读Java源码 阅读java的真实体会
刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比 ...
- [收藏] Java源码阅读的真实体会
收藏自http://www.iteye.com/topic/1113732 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我 ...
- java源码阅读Hashtable
1类签名与注释 public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, C ...
- Java源码阅读Stack
Stack(栈)实现了一个后进先出(LIFO)的数据结构.该类继承了Vector类,是通过调用父类Vector的方法实现基本操作的. Stack共有以下五个操作: put:将元素压入栈顶. pop:弹 ...
- Java源码阅读顺序
阅读顺序参考链接:https://blog.csdn.net/qq_21033663/article/details/79571506 阅读源码:JDK 8 计划阅读的package: 1.java. ...
- java源码阅读LinkedBlockingQueue
1类签名与简介 public class LinkedBlockingQueue<E> extends AbstractQueue<E> implements Blocking ...
- java源码阅读ArrayBlockingQueue
1类签名与简介 public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQ ...
随机推荐
- [Leetcode Week2]Merge Intervals
Merge Intervals题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/merge-intervals/description/ Descript ...
- kuangbin带你飞 并查集 题解
做这套题之前一直以为并查集是很简单的数据结构. 做了才发现自己理解太不深刻.只看重片面的合并集合.. 重要的时发现每个集合的点与这个根的关系,这个关系可以做太多事情了. 题解: POJ 2236 Wi ...
- 网络知识===TCP/UDP的区别
TCP(传输控制协议,Transmission Control Protocol): 1)提供IP环境下的数据可靠传输(一台计算机发出的字节流会无差错的发往网络上的其他计算机,而且计算机A接收数据包的 ...
- python基础===两个list合并成一个dict的方法
def Run(): list2 = [, , , , ]; list3 = ["a", "b", "c", "d",& ...
- linux内核分析笔记----中断和中断处理程序【转】
转自:http://www.cnblogs.com/hanyan225/archive/2011/07/17/2108609.html 中断还是中断,我讲了很多次的中断了,今天还是要讲中断,为啥呢?因 ...
- GLE api
# -*- coding: utf-8 -*-# @Time : 2018/03/09 12:25# @Author : cxa# @File : gle.py# @Software: PyCharm ...
- JSP(1) - JSP简介、原理、语法 - 小易Java笔记
1.JSP简介 (1)JSP的全称是Java Server Pages(运行在服务器端的页面),实际就是Servlet(学习JSP的关键就是时刻联想到Servlet) (2)JSP.Servlet各自 ...
- python通过多进程实行多任务
#原创,转载请联系 在开始之前,我们要知道什么是进程.道理很简单,你平时电脑打开QQ客户端,就是一个进程.再打开一个QQ客户端,又是一个进程.那么,在python中如何用一篇代码就可以开启几个进程呢? ...
- 使用厂商MIB库查找设备OID值 并实施监控的方法
http://chuansong.me/n/2700132 https://wenku.baidu.com/view/eeaeb1d680eb6294dd886cc7.html
- python strip_tags 支持保留指定标签
#coding:utf-8 import re def strip_tags(string, allowed_tags=''): if allowed_tags != '': # Get a list ...