给jdk写注释系列之jdk1.6容器(12)-PriorityQueue源码解析
(2)堆总是一棵完全树。


public class PriorityQueue<E> extends AbstractQueue<E>
implements java.io.Serializable {
// 默认初始化大小
privatestaticfinalintDEFAULT_INITIAL_CAPACITY = 11; // 用数组实现的二叉堆,下面的英文注释确认了我们前面的说法。
/**
* 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.
*/
private transient Object[] queue ; // 队列的元素数量
private int size = 0; // 比较器
private final Comparator<? super E> comparator; // 修改版本
private transient int modCount = 0;
/**
* 默认构造方法,使用默认的初始大小来构造一个优先队列,比较器comparator为空,这里要求入队的元素必须实现Comparator接口
*/
public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
} /**
* 使用指定的初始大小来构造一个优先队列,比较器comparator为空,这里要求入队的元素必须实现Comparator接口
*/
public PriorityQueue( int initialCapacity) {
this(initialCapacity, null);
} /**
* 使用指定的初始大小和比较器来构造一个优先队列
*/
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
// 初始大小不允许小于1
if (initialCapacity < 1)
throw new IllegalArgumentException();
// 使用指定初始大小创建数组
this.queue = new Object[initialCapacity];
// 初始化比较器
this.comparator = comparator;
} /**
* 构造一个指定Collection集合参数的优先队列
*/
public PriorityQueue(Collection<? extends E> c) {
// 从集合c中初始化数据到队列
initFromCollection(c);
// 如果集合c是包含比较器Comparator的(SortedSet/PriorityQueue),则使用集合c的比较器来初始化队列的Comparator
if (c instanceof SortedSet)
comparator = (Comparator<? super E>)
((SortedSet<? extends E>)c).comparator();
else if (c instanceof PriorityQueue)
comparator = (Comparator<? super E>)
((PriorityQueue<? extends E>)c).comparator();
// 如果集合c没有包含比较器,则默认比较器Comparator为空
else {
comparator = null;
// 调用heapify方法重新将数据调整为一个二叉堆
heapify();
}
} /**
* 构造一个指定PriorityQueue参数的优先队列
*/
public PriorityQueue(PriorityQueue<? extends E> c) {
comparator = (Comparator<? super E>)c.comparator();
initFromCollection(c);
} /**
* 构造一个指定SortedSet参数的优先队列
*/
public PriorityQueue(SortedSet<? extends E> c) {
comparator = (Comparator<? super E>)c.comparator();
initFromCollection(c);
} /**
* 从集合中初始化数据到队列
*/
private void initFromCollection(Collection<? extends E> c) {
// 将集合Collection转换为数组a
Object[] a = c.toArray();
// If c.toArray incorrectly doesn't return Object[], copy it.
// 如果转换后的数组a类型不是Object数组,则转换为Object数组
if (a.getClass() != Object[].class)
a = Arrays. copyOf(a, a.length, Object[]. class);
// 将数组a赋值给队列的底层数组queue
queue = a;
// 将队列的元素个数设置为数组a的长度
size = a.length ;
}


/**
* 添加一个元素
*/
public boolean add(E e) {
return offer(e);
} /**
* 入队
*/
public boolean offer(E e) {
// 如果元素e为空,则排除空指针异常
if (e == null)
throw new NullPointerException();
// 修改版本+1
modCount++;
// 记录当前队列中元素的个数
int i = size ;
// 如果当前元素个数大于等于队列底层数组的长度,则进行扩容
if (i >= queue .length)
grow(i + 1);
// 元素个数+1
size = i + 1;
// 如果队列中没有元素,则将元素e直接添加至根(数组小标0的位置)
if (i == 0)
queue[0] = e;
// 否则调用siftUp方法,将元素添加到尾部,进行上移判断
else
siftUp(i, e);
return true;
}
/**
* 数组扩容
*/
private void grow(int minCapacity) {
// 如果最小需要的容量大小minCapacity小于0,则说明此时已经超出int的范围,则抛出OutOfMemoryError异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 记录当前队列的长度
int oldCapacity = queue .length;
// Double size if small; else grow by 50%
// 如果当前队列长度小于64则扩容2倍,否则扩容1.5倍
int newCapacity = ((oldCapacity < 64)?
((oldCapacity + 1) * 2):
((oldCapacity / 2) * 3));
// 如果扩容后newCapacity超出int的范围,则将newCapacity赋值为Integer.Max_VALUE
if (newCapacity < 0) // overflow
newCapacity = Integer. MAX_VALUE;
// 如果扩容后,newCapacity小于最小需要的容量大小minCapacity,则按找minCapacity长度进行扩容
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// 数组copy,进行扩容
queue = Arrays.copyOf( queue, newCapacity);
}
/**
* 上移,x表示新插入元素,k表示新插入元素在数组的位置
*/
private void siftUp(int k, E x) {
// 如果比较器comparator不为空,则调用siftUpUsingComparator方法进行上移操作
if (comparator != null)
siftUpUsingComparator(k, x);
// 如果比较器comparator为空,则调用siftUpComparable方法进行上移操作
else
siftUpComparable(k, x);
} private void siftUpComparable(int k, E x) {
// 比较器comparator为空,需要插入的元素实现Comparable接口,用于比较大小
Comparable<? super E> key = (Comparable<? super E>) x;
// k>0表示判断k不是根的情况下,也就是元素x有父节点
while (k > 0) {
// 计算元素x的父节点位置[(n-1)/2]
int parent = (k - 1) >>> 1;
// 取出x的父亲e
Object e = queue[parent];
// 如果新增的元素k比其父亲e大,则不需要"上移",跳出循环结束
if (key.compareTo((E) e) >= 0)
break;
// x比父亲小,则需要进行"上移"
// 交换元素x和父亲e的位置
queue[k] = e;
// 将新插入元素的位置k指向父亲的位置,进行下一层循环
k = parent;
}
// 找到新增元素x的合适位置k之后进行赋值
queue[k] = key;
} // 这个方法和上面的操作一样,不多说了
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;
}



/**
* 删除并返回队头的元素,如果队列为空则抛出NoSuchElementException异常(该方法在AbstractQueue中)
*/
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
} /**
* 删除并返回队头的元素,如果队列为空则返回null
*/
public E poll() {
// 队列为空,返回null
if (size == 0)
return null;
// 队列元素个数-1
int s = --size ;
// 修改版本+1
modCount++;
// 队头的元素
E result = (E) queue[0];
// 队尾的元素
E x = (E) queue[s];
// 先将队尾赋值为null
queue[s] = null;
// 如果队列中不止队尾一个元素,则调用siftDown方法进行"下移"操作
if (s != 0)
siftDown(0, x);
return result;
} /**
* 上移,x表示队尾的元素,k表示被删除元素在数组的位置
*/
private void siftDown(int k, E x) {
// 如果比较器comparator不为空,则调用siftDownUsingComparator方法进行下移操作
if (comparator != null)
siftDownUsingComparator(k, x);
// 比较器comparator为空,则调用siftDownComparable方法进行下移操作
else
siftDownComparable(k, x);
} private void siftDownComparable(int k, E x) {
// 比较器comparator为空,需要插入的元素实现Comparable接口,用于比较大小
Comparable<? super E> key = (Comparable<? super E>)x;
// 通过size/2找到一个没有叶子节点的元素
int half = size >>> 1; // loop while a non-leaf
// 比较位置k和half,如果k小于half,则k位置的元素就不是叶子节点
while (k < half) {
// 找到根元素的左孩子的位置[2n+1]
int child = (k << 1) + 1; // assume left child is least
// 左孩子的元素
Object c = queue[child];
// 找到根元素的右孩子的位置[2(n+1)]
int right = child + 1;
// 如果左孩子大于右孩子,则将c复制为右孩子的值,这里也就是找出左右孩子哪个最小
if (right < size &&
((Comparable<? super E>) c).compareTo((E) queue [right]) > 0)
c = queue[child = right];
// 如果队尾元素比根元素孩子都要小,则不需"下移",结束
if (key.compareTo((E) c) <= 0)
break;
// 队尾元素比根元素孩子都大,则需要"下移"
// 交换跟元素和孩子c的位置
queue[k] = c;
// 将根元素位置k指向最小孩子的位置,进入下层循环
k = child;
}
// 找到队尾元素x的合适位置k之后进行赋值
queue[k] = key;
} // 这个方法和上面的操作一样,不多说了
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;
}
/**
* Establishes the heap invariant (described above) in the entire tree,
* assuming nothing about the order of the elements prior to the call.
*/
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}




private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
给jdk写注释系列之jdk1.6容器(12)-PriorityQueue源码解析的更多相关文章
- 给jdk写注释系列之jdk1.6容器(7)-TreeMap源码解析
TreeMap是基于红黑树结构实现的一种Map,要分析TreeMap的实现首先就要对红黑树有所了解. 要了解什么是红黑树,就要了解它的存在主要是为了解决什么问题,对比其他数据结构比如数组,链 ...
- 给jdk写注释系列之jdk1.6容器(6)-HashSet源码解析&Map迭代器
今天的主角是HashSet,Set是什么东东,当然也是一种java容器了. 现在再看到Hash心底里有没有会心一笑呢,这里不再赘述hash的概念原理等一大堆东西了(不懂得需要先回去看下Has ...
- 给jdk写注释系列之jdk1.6容器(5)-LinkedHashMap源码解析
前面分析了HashMap的实现,我们知道其底层数据存储是一个hash表(数组+单向链表).接下来我们看一下另一个LinkedHashMap,它是HashMap的一个子类,他在HashMap的基础上维持 ...
- 给jdk写注释系列之jdk1.6容器(4)-HashMap源码解析
前面了解了jdk容器中的两种List,回忆一下怎么从list中取值(也就是做查询),是通过index索引位置对不对,由于存入list的元素时安装插入顺序存储的,所以index索引也就是插入的次序. M ...
- 给jdk写注释系列之jdk1.6容器(2)-LinkedList源码解析
LinkedList是基于链表结构的一种List,在分析LinkedList源码前有必要对链表结构进行说明. 1.链表的概念 链表是由一系列非连续的节点组成的存储结构,简单分下类的话,链 ...
- 给jdk写注释系列之jdk1.6容器(1)-ArrayList源码解析
工作中经常听到别人讲“容器”,各种各样的容器,话说到底什么是容器,通俗的讲“容器就是用来装东西的器皿,比如:水桶就是用来盛水的,水桶就是一个容器.” ok,在我们写程序的时候常常要对大量的对象进行管理 ...
- 给jdk写注释系列之jdk1.6容器(13)-总结篇之Java集合与数据结构
是的,这篇blogs是一个总结篇,最开始的时候我提到过,对于java容器或集合的学习也可以看做是对数据结构的学习与应用.在前面我们分析了很多的java容器,也接触了好多种常用的数据结构,今天 ...
- 给jdk写注释系列之jdk1.6容器(11)-Queue之ArrayDeque源码解析
前面讲了Stack是一种先进后出的数据结构:栈,那么对应的Queue是一种先进先出(First In First Out)的数据结构:队列. 对比一下Stack,Queue是一种先进先出的容 ...
- 给jdk写注释系列之jdk1.6容器(10)-Stack&Vector源码解析
前面我们已经接触过几种数据结构了,有数组.链表.Hash表.红黑树(二叉查询树),今天再来看另外一种数据结构:栈. 什么是栈呢,我就不找它具体的定义了,直接举个例子,栈就相当于一个很窄的木桶 ...
随机推荐
- 常用抓包指令for wireshark or tcpdump
1, tshark -ni eth0 -R "tcp.dstport eq 8080" [wireshark 抓指定协议端口数据包] 2, tcpdump -XvvennSs 0 ...
- Java设计模式系列之适配器模式
适配器模式的定义 将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.(就类似于我们充电器的转接头将220V的电压转换成我们的手机端 ...
- ADO.NET 快速入门(十一):连接池
这个示例演示了如何构建一个到数据源的连接池.你可以通过连接池部署高性能的应用程序.本例中使用连接串创建连接池,并且由 SqlConnection 自动管理. string connString; ...
- 在Mac OS X 10.9上安装nginx
1. 安装PCRE Download latest PCRE. After download go to download directory from terminal. $ cd ~/Downlo ...
- Codeforces Gym 100342J Problem J. Triatrip bitset 求三元环的数量
Problem J. TriatripTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100342/att ...
- [PLL][PM]锁相环模拟相位解调
%锁相环测试 %模拟相位解调 clear close all clc fs=1000; %采样率 tend=100; t=0:1/fs:tend; t(end)=[]; fc=1; %载波频偏 fb= ...
- 解决IE下jquery ajax无法获得最新数据的问题(IE缓存)
今天修改一个bug,利用ajax查询数据,在谷歌浏览器下可以获取到最新数据,而在IE中获得是旧数据,无法获得最新的数据,经查资料,才发现时IE缓存再作怪. 发现此ajax请求用的get方式,每次请求的 ...
- 一起聊聊 Swift 3.0
Swift3.0将会给我们带来哪些改变: 1. 稳定二进制接口(ABI) ABI是什么呢?API大家都知道是应用程序接口 API只是提供函数签名 而ABI是系统和语言层面的 如果ABI稳定 意味着以后 ...
- wkhtmltopdf 将网页生成pdf文件
先安装依赖 yum install fontconfig libXrender libXext xorg-x11-fonts-Type1 xorg-x11-fonts-75dpi freetype l ...
- android151 笔记 3
34. 对android虚拟机的理解,包括内存管理机制垃圾回收机制. 虚拟机很小,空间很小,谈谈移动设备的虚拟机的大小限制 16M , 谈谈加载图片的时候怎么处理大图片的,压缩. 垃圾回收,没有引用的 ...