给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表.红黑树(二叉查询树),今天再来看另外一种数据结构:栈. 什么是栈呢,我就不找它具体的定义了,直接举个例子,栈就相当于一个很窄的木桶 ...
随机推荐
- 转载-MySQL 加锁处理分析
MySQL 加锁处理分析 发表于 2013 年 12 月 13 日 由 hedengcheng 1 背景 1 1.1 MVCC:Snapshot Read vs Current Re ...
- Spark RDD概念学习系列之Spark的算子的作用(十四)
Spark的算子的作用 首先,关于spark算子的分类,详细见 http://www.cnblogs.com/zlslch/p/5723857.html 1.Transformation 变换/转换算 ...
- PHP操作cookie函数:setcookie()与setrawcookie()
PHP setcookie() 函数向客户端发送一个 HTTP cookie.cookie 是由服务器发送到浏览器的变量.cookie 通常是服务器嵌入到用户计算机中的小文本文件.每当计算机通过浏览器 ...
- [iOS 多线程 & 网络 - 4.0] - AFN框架简单使用
A.AFN基本知识 1.概念 AFNetworking 是对NSURLConnection的封装 运行效率没有ASI高(因为ASI基于CFNetwork),但是使用简单 AFN支持ARC B. ...
- 关闭Linux里边的selinux
首先我们可以用命令来查看selinux的状态 getenforce 这个命令可以查看到selinux的状态,当前可以看到是关闭状态的. 还有一个命令也可以查看出selinux的状态. sest ...
- ios页面传值的几种方法
1.属性2.方法3.代理方法4.SharedApplication5.NSUserdefault6.通过一个单例的class来传递 属性这种方法传值挺方便的,只需要拿到它的指针,如果重新声明一个指针, ...
- ASP.NET Web Api返回对象类型为JSON还是XML
在Umbraco平台上开发过程中,我用WebApi返回JSON result给前端 前端使用React调用这个web api来获取JSON result 我写的web api方法是返回JSON 类型的 ...
- C语言单向循环链表解决约瑟夫问题
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,4 ...
- code::blocks编译多文件 没有定义的引用
code::blocks是一款据说灰常强大的IDE,以前虽然也经常使用,但一没用过高度功能,二来没用它写过工程性的东西,简单点说就是一个以上的源文件并且加入其他非标准的头文件,今天想做一个多文件的语法 ...
- PIL在windwos系统下Image.show无法显示图片问题的解决方法
环境:1.win7 64位 2.python 2.7.8 3.PIL-1.1.7.win32-py2.7 在运行一下例子时候出现问题: #-*-coding:utf-8-*- __author__ = ...