Java优先队列PriorityQueue的各种打开方式以及一些你不知道的细节
首先我们知道用PriorityQueue这个类创建的对象是一个集合,然后调用api可以将一个个对象添加入集合,然后再通过api遍历时,插入的元素就从小到大排序输出,真的太神奇了!
下面将介绍优先队列的默认用法以及我想自己写个类,然后扔到优先队列中让它也能从小到大排序怎么做?从大到小嘞?又或者我又想用系统的类比如String,但是我想按字典序从大到小能做吗?
题外话:每插入一个就会自动排序,这么强大的功能,效率一定不高吧?但如果你学过数据结构,想一下建堆的过程,以及堆排序中添加与调整堆的过程你就会发现,堆排序与优先队列每次插入排序的功能是那么契合~
下面的讲解我尝试用一种循序渐进的方式讲述我知道的优先队列,有经验的读者可能会觉得很啰嗦,但我还是觉得这能帮助你梳理一下知识点
优先队列的默认用法—从小到大排序
我们先新建一个优先队列,然后扔四个字符进去,然后用迭代器Iterator或者for-each方式遍历打印,或者直接用println()打印,结果并非从小到大,而是(层序遍历的堆,总之不是你要的~),事实上只有通过优先队列定义的api才能按从小到大取出元素,比如remove方法
此时如果你对:
1.为什么能用for each形式可以打印集合有疑惑:这就要追根溯源到这个PriorityQueue类的来源,Java集合框架是一个大家族,它们由很多的接口定义了不同的功能,再由很多抽象类去逐步实现这些接口的功能,抽象类一代代继承,最后得到如优先队列这样的最终实现类,而for each的遍历方式是它的最上面的祖先Iterable接口中的一个方法,任何实现了Iterable接口的类都能用迭代器进行遍历
2.为什么能System.out.println(XX);直接打印一个XX对象有疑惑:事实上只有实现了toString方法的类,才能在调用这个方法的时候转化成字符串再打印
回到我们的程序
public class PriorityQueueTest {
public static void main(String[] args) {
//这里String类型默认实现Comparable接口的 就是按字典序排序 从小大到
//上面这个注释你可能不太明白,看下去就会明白了~,现在无视它
var pq = new PriorityQueue<String>();
pq.add("B");
pq.add("D");
pq.add("A");
pq.add("C");
//通过迭代器和for each,以及println输出的顺序是(层序遍历的堆)
System.out.println(pq);
for (String s : pq)
System.out.print(s);
System.out.println();
Iterator iter = pq.iterator();
while (iter.hasNext())
System.out.print(iter.next());
System.out.println();
//isEmpty方法是AbstractCollection抽象类中的
while (!pq.isEmpty())
//remove方法按照优先队列中定义的每次返回最小的元素,并删去该值
//本例的最小是字典序最小,但是这个最小的概念是可以通过用户自己定义的
//怎么定义下面会讲
System.out.print(pq.remove());
System.out.println();
System.out.println(pq);
}
}
输出结果
[A, C, B, D]
ACBD
ACBD
//很显然上三种打印的顺序并非我们需要的(打印的是层序的堆),没有实现从小到大排序
ABCD
[]
对String类用优先队列从大到小排序
现在需要补充一个知识点:想要往优先队列里放入一个对象,它就默认会去排序调整它在集合中的位置,与Java集合框架中的其他实现类一样,而一切涉及排序功能的实现类,我们放入集合中的元素都必须实现了Comparable接口,或者在调用构造器时提供了Comparator对象为参数,这句话接下来会用示例讲解~
先来看一下String对象的源码,它能直接放入PriorityQueue类是因为它实现了Compar