- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章解析Java中PriorityQueue优先级队列结构的源码及用法由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
1、PriorityQueue的数据结构 。
JDK7中PriorityQueue(优先级队列)的数据结构是二叉堆。准确的说是一个最小堆.
二叉堆是一个特殊的堆, 它近似完全二叉树。二叉堆满足特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆。 当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆。 当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。 下图是一个最大堆 。
priorityQueue队头就是给定顺序的最小元素.
priorityQueue不允许空值且不支持non-comparable的对象。priorityQueue要求使用Comparable和Comparator接口给对象排序,并且在排序时会按照优先级处理其中的元素.
priorityQueue的大小是无限制的(unbounded), 但在创建时可以指定初始大小。当增加队列元素时,队列会自动扩容.
priorityQueue不是线程安全的, 类似的PriorityBlockingQueue是线程安全的.
我们知道队列是遵循先进先出(First-In-First-Out)模式的,但有些时候需要在队列中基于优先级处理对象。举个例子,比方说我们有一个每日交易时段生成股票报告的应用程序,需要处理大量数据并且花费很多处理时间。客户向这个应用程序发送请求时,实际上就进入了队列。我们需要首先处理优先客户再处理普通用户。在这种情况下,Java的PriorityQueue(优先队列)会很有帮助.
PriorityQueue是基于优先堆的一个无界队列,这个优先队列中的元素可以默认自然排序或者通过提供的Comparator(比较器)在队列实例化的时排序。 优先队列不允许空值,而且不支持non-comparable(不可比较)的对象,比如用户自定义的类。优先队列要求使用Java Comparable和Comparator接口给对象排序,并且在排序时会按照优先级处理其中的元素.
优先队列的头是基于自然排序或者Comparator排序的最小元素。如果有多个对象拥有同样的排序,那么就可能随机地取其中任意一个。当我们获取队列时,返回队列的头对象.
优先队列的大小是不受限制的,但在创建时可以指定初始大小。当我们向优先队列增加元素的时候,队列大小会自动增加.
PriorityQueue是非线程安全的,所以Java提供了PriorityBlockingQueue(实现BlockingQueue接口)用于Java多线程环境.
2、PriorityQueue源码分析 。
成员:
1
2
|
priavte
transient
Object[] queue;
private
int
size =
0
;
|
1.PriorityQueue构造小顶堆的过程 。
这里我们以priorityQueue构造器传入一个容器为参数PriorityQueue(Collecntion<? extends E>的例子:
构造小顶堆的过程大体分两步:
复制容器数据,检查容器数据是否为null 。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
private
void
initElementsFromCollection(Collection<?
extends
E> c) {
Object[] a = c.toArray();
// If c.toArray incorrectly doesn't return Object[], copy it.
if
(a.getClass() != Object[].
class
)
a = Arrays.copyOf(a, a.length, Object[].
class
);
int
len = a.length;
if
(len ==
1
||
this
.comparator !=
null
)
for
(
int
i =
0
; i < len; i++)
if
(a[i] ==
null
)
throw
new
NullPointerException();
this
.queue = a;
this
.size = a.length;
}
|
调整,使数据满足小顶堆的结构。 首先介绍两个调整方式siftUp和siftDown 。
siftDown: 在给定初始化元素的时候,要调整元素,使其满足最小堆的结构性质。因此不停地从上到下将元素x的键值与孩子比较并做交换,直到找到元素x的键值小于等于孩子的键值(即保证它比其左右结点值小),或者是下降到叶子节点为止。 例如如下的示意图,调整9这个节点:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
private
void
siftDownComparable(
int
k, E x) {
Comparable<?
super
E> key = (Comparable<?
super
E>)x;
int
half = size >>>
1
;
// size/2是第一个叶子结点的下标
//只要没到叶子节点
while
(k < half) {
int
child = (k <<
1
) +
1
;
// 左孩子
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;
}
|
siftUp: priorityQueue每次新增加一个元素的时候是将新元素插入对尾的。因此,应该与siftDown有同样的调整过程,只不过是从下(叶子)往上调整。 例如如下的示意图,填加key为3的节点:
1
2
3
4
5
6
7
8
9
10
11
12
|
private
void
siftUpComparable(
int
k, E x) {
Comparable<?
super
E> key = (Comparable<?
super
E>) x;
while
(k >
0
) {
int
parent = (k -
1
) >>>
1
;
//获取parent下标
Object e = queue[parent];
if
(key.compareTo((E) e) >=
0
)
break
;
queue[k] = e;
k = parent;
}
queue[k] = key;
}
|
总体的建立小顶堆的过程就是:
1
2
3
4
|
private
void
initFromCollection(Collection<?
extends
E> c) {
initElementsFromCollection(c);
heapify();
}
|
其中heapify就是siftDown的过程.
2.PriorityQueue容量扩容过程 。
从实例成员可以看出,PriorityQueue维护了一个Object[], 因此它的扩容方式跟顺序表ArrayList相差不多。 这里只给出grow方法的源码 。
1
2
3
4
5
6
7
8
9
10
11
|
private
void
grow(
int
minCapacity) {
int
oldCapacity = queue.length;
// Double size if small; else grow by 50%
int
newCapacity = oldCapacity + ((oldCapacity <
64
) ?
(oldCapacity +
2
) :
(oldCapacity >>
1
));
// overflow-conscious code
if
(newCapacity - MAX_ARRAY_SIZE >
0
)
newCapacity = hugeCapacity(minCapacity);
queue = Arrays.copyOf(queue, newCapacity);
}
|
可以看出,当数组的Capacity不大的时候,每次扩容也不大。当数组容量大于64的时候,每次扩容double.
3、PriorityQueue的应用 。
eg1: 这里给出一个最简单的应用:从动态数据中求第K个大的数。 思路就是维持一个size = k 的小顶堆.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//data是动态数据
//heap维持动态数据的堆
//res用来保存第K大的值
public
boolean
kthLargest(
int
data,
int
k, PriorityQueue<Integer> heap,
int
[] res) {
if
(heap.size() < k) {
heap.offer(data);
if
(heap.size() == k) {
res[
0
] = heap.peek();
return
true
;
}
return
false
;
}
if
(heap.peek() < data) {
heap.poll();
heap.offer(data);
}
res[
0
] = heap.peek();
return
true
;
}
|
eg2: 我们有一个用户类Customer,它没有提供任何类型的排序。当我们用它建立优先队列时,应该为其提供一个比较器对象.
Customer.java 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package
com.journaldev.collections;
public
class
Customer {
private
int
id;
private
String name;
public
Customer(
int
i, String n){
this
.id=i;
this
.name=n;
}
public
int
getId() {
return
id;
}
public
String getName() {
return
name;
}
}
|
我们使用Java随机数生成随机用户对象。对于自然排序,我们使用Integer对象,这也是一个封装过的Java对象。 下面是最终的测试代码,展示如何使用PriorityQueue:
PriorityQueueExample.java 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
package
com.journaldev.collections;
import
java.util.Comparator;
import
java.util.PriorityQueue;
import
java.util.Queue;
import
java.util.Random;
public
class
PriorityQueueExample {
public
static
void
main(String[] args) {
//优先队列自然排序示例
Queue<Integer> integerPriorityQueue =
new
PriorityQueue<>(
7
);
Random rand =
new
Random();
for
(
int
i=
0
;i<
7
;i++){
integerPriorityQueue.add(
new
Integer(rand.nextInt(
100
)));
}
for
(
int
i=
0
;i<
7
;i++){
Integer in = integerPriorityQueue.poll();
System.out.println(
"Processing Integer:"
+in);
}
//优先队列使用示例
Queue<Customer> customerPriorityQueue =
new
PriorityQueue<>(
7
, idComparator);
addDataToQueue(customerPriorityQueue);
pollDataFromQueue(customerPriorityQueue);
}
//匿名Comparator实现
public
static
Comparator<Customer> idComparator =
new
Comparator<Customer>(){
@Override
public
int
compare(Customer c1, Customer c2) {
return
(
int
) (c1.getId() - c2.getId());
}
};
//用于往队列增加数据的通用方法
private
static
void
addDataToQueue(Queue<Customer> customerPriorityQueue) {
Random rand =
new
Random();
for
(
int
i=
0
; i<
7
; i++){
int
id = rand.nextInt(
100
);
customerPriorityQueue.add(
new
Customer(id,
"Pankaj "
+id));
}
}
//用于从队列取数据的通用方法
private
static
void
pollDataFromQueue(Queue<Customer> customerPriorityQueue) {
while
(
true
){
Customer cust = customerPriorityQueue.poll();
if
(cust ==
null
)
break
;
System.out.println(
"Processing Customer with ID="
+cust.getId());
}
}
}
|
注意我用实现了Comparator接口的Java匿名类,并且实现了基于id的比较器。 当我运行以上测试程序时,我得到以下输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
Processing Integer:9
Processing Integer:16
Processing Integer:18
Processing Integer:25
Processing Integer:33
Processing Integer:75
Processing Integer:77
Processing Customer with ID=6
Processing Customer with ID=20
Processing Customer with ID=24
Processing Customer with ID=28
Processing Customer with ID=29
Processing Customer with ID=82
Processing Customer with ID=96
|
从输出结果可以清楚的看到,最小的元素在队列的头部因而最先被取出。如果不实现Comparator,在建立customerPriorityQueue时会抛出ClassCastException.
1
2
3
4
5
6
7
|
Exception in thread "main" java.lang.ClassCastException: com.journaldev.collections.Customer cannot be cast to java.lang.Comparable
at java.util.PriorityQueue.siftUpComparable(PriorityQueue.java:633)
at java.util.PriorityQueue.siftUp(PriorityQueue.java:629)
at java.util.PriorityQueue.offer(PriorityQueue.java:329)
at java.util.PriorityQueue.add(PriorityQueue.java:306)
at com.journaldev.collections.PriorityQueueExample.addDataToQueue(PriorityQueueExample.java:45)
at com.journaldev.collections.PriorityQueueExample.main(PriorityQueueExample.java:25)
|
。
最后此篇关于解析Java中PriorityQueue优先级队列结构的源码及用法的文章就讲到这里了,如果你想了解更多关于解析Java中PriorityQueue优先级队列结构的源码及用法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
采用另一个优先级队列的 java API PriorityQueue 构造函数是否会破坏参数?如果是这样,它的clone()方法足以创建浅拷贝吗? 最佳答案 不,它不是破坏性的。几乎所有集合类都有复制
我正在尝试复制一个 PriorityQueue 对象。 我的目标是在不修改我原来的 PriorityQueue 的情况下更改我的 Copy 的某些对象 为了这样做,我复制了我的 PriorityQue
我正在解决leetcode的Merge K Sorted Lists problem . 使用 Python2 的 Queue 模块中的 PriorityQueue 的相同算法会为 Python3 的
当 PriorityQueue.size() > 0 在 Android 上时,当 PriorityQueue.peek() 返回 null 时,我遇到了问题。 我认为这可能是设备问题。有人有什么想法
我有一个asyncio.PriorityQueue,用作网络爬虫的URL队列,当我调用url_queue.get时,得分最低的URL首先从队列中删除()。当队列达到 maxsize 项时,默认行为是阻
完全二叉树 一棵深度为k的有n个结点的 二叉树 ,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与 满二叉树 中编号为i的结点在二叉树中的位置相同,则这棵
我很难理解 priorityqueue 如何使用 compareTo 方法对其内容进行排序。 我在上一门名为 Node.js 的类(class)。它有 4 个字段。 private char char
我目前有一种方法使用 scala.collection.mutable.PriorityQueue 按特定顺序组合元素。例如代码看起来有点像这样: def process[A : Ordering]
没有提供自定义比较器,优先级队列按升序插入元素,但是,在删除特定元素后,顺序会发生变化。 PriorityQueue pq = new PriorityQueue<>(); pq.add(10); p
这个问题已经有答案了: The built-in iterator for java's PriorityQueue does not traverse the data structure in a
已关闭。这个问题是 not reproducible or was caused by typos 。目前不接受答案。 这个问题是由拼写错误或无法再重现的问题引起的。虽然类似的问题可能是 on-top
我指的是此博客上列出的代码:https://strstr.io/Leetcode1054-Distant-Barcodes/ 我在这里复制这段代码 class Solution { publi
这个问题已经有答案了: Why does PriorityQueue.toString return the wrong element order? [duplicate] (4 个回答) 已关闭
我有一个比较器类 NComparator,它比较 2 个 Node 对象并返回 1、-1 或 0。 我初始化了一个初始容量为 100 的 PriorityQueue 和那个 NComparator。
当项目是整数与字符串时,PriorityQueue 的不同行为让我非常困惑。但在解决这个问题之前,我想了解以下行为(使用项目作为整数)。 假设我有一个包含以下数据的优先级队列(对于每个元素,第一个值是
我有使用 PriorityQueue 的程序。 poll() 没有给出队列中的所有值。 class Coffee { public static void main(String[] args
所以我正在尝试构建我的第一个 prim 算法,为此我根据其权重按优先级对边缘进行排序。 所以我认为如果我使用优先级队列会很有帮助,为此我需要让我的边缘实现 Comparable<> 接口(interf
编译器(Java 8)提示以下代码没有合适的构造函数: PriorityQueue heap = new PriorityQueue((ListNode n1, ListNode n2) -> n1.
我有一个优先级队列,我在其中添加一个节点对象,其中节点应按它们包含的值排序。由于某种原因,优先级队列不会在添加时对节点进行排序。如果有人能发现其中的问题或有任何指导,我很感激。这是一个简短的示例: P
这是我在这里发表的第一篇文章,因此请随时为我指出关于在这里提出问题的正确方向。 我的问题出在 java.util.PriorityQueue 上。 我有一个初始化的队列; myComparab
我是一名优秀的程序员,十分优秀!