前言
我们知道队列是遵循先进先出(First-In-First-Out)模式的,但有些时候需要在队列中基于优先级处理对象。
为什么优先级队列,其实很好理解。比如银行的VIP客户、各大机场的VIP客户的优先登机等,都是优先级队列的体现。而正常排队的都属于普通队列~
PriorityQueue
PriorityQueue类在Java1.5中引入的,它是Java集合框架的一部分。PriorityQueue是基于优先堆的一个无界队列,它是一个Queue
默认情况下它 根据自然排序,当然我们也可以定制比较器,自行自定义排序,从而实现自己的优先级逻辑。
// @since 1.5 public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable { // 构造函数 public PriorityQueue() { this(DEFAULT_INITIAL_CAPACITY, null); } public PriorityQueue(int initialCapacity) { this(initialCapacity, null); } //@since 1.8 public PriorityQueue(Comparator<? super E> comparator) { this(DEFAULT_INITIAL_CAPACITY, comparator); } // 自己指定初始化因子,指定比较器 public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) { // initialCapacity 不能小于1 if (initialCapacity < 1) throw new IllegalArgumentException(); this.queue = new Object[initialCapacity]; this.comparator = comparator; } // public PriorityQueue(Collection<? extends E> c) {...} public PriorityQueue(SortedSet<? extends E> c) {...} ... } // @since 1.5 AbstractQueue这个类也是1.5后出现的 我们发现它还继承自AbstractCollection 所以它也是个Collection public abstract class AbstractQueue<E> extends AbstractCollection<E> implements Queue<E> { ... }
Demo Show:
public static void main(String[] args) { PriorityQueue<String> priorityQueue = new PriorityQueue<>(); priorityQueue.add("orange"); priorityQueue.add("fig"); priorityQueue.add("watermelon"); priorityQueue.add("lemon"); while (priorityQueue.size() != 0) { System.out.println(priorityQueue.remove()); } }
输出:
fig lemon orange watermelon
没有指定比较器,所以就按照自然排序的规则排了。下面我们自己指定一个比较器试试:
// 指定一个倒序输出的比较器 PriorityQueue<String> priorityQueue = new PriorityQueue<>(Comparator.reverseOrder());
输出:
fig lemon orange watermelon
因此我们看到优先级队列的使用,使用起来还是非常的简单的。
优先队列不允许空值,而且不支持non-comparable(不可比较)的对象,比如用户自定义的类。
注意:此处需要注意的是,你是基于某个字段做优先级排序,只需要那个字段可比较即可,而不需要整个类都实现比较器接口
优先队列的头是基于自然排序或者Comparator排序的最小元素。如果有多个对象拥有同样的排序,那么就可能随机地取其中任意一个。当我们获取队列时,返回队列的头对象。
PriorityQueue是非线程安全的,所以Java提供了PriorityBlockingQueue(实现BlockingQueue接口)用于Java多线程环境。
PriorityBlockingQueue
在之前有篇博文:
【小家java】BlockingQueue阻塞队列详解以及5大实现(ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue…)
本文重点不介绍它阻塞的特性,而是介绍它优先级队列的使用办法。
它是一个无界有序的阻塞队列,排序规则和之前介绍的PriorityQueue一致,只是增加了阻塞操作。同样的该队列不支持插入null元素,同时不支持插入非comparable的对象。
该类不保证同等优先级的元素顺序,如果你想要强制顺序,就需要考虑自定义顺序或者是Comparator使用第二个比较属性
public class PriorityBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { public PriorityBlockingQueue() { this(DEFAULT_INITIAL_CAPACITY, null); } public PriorityBlockingQueue(int initialCapacity) { this(initialCapacity, null); } public PriorityBlockingQueue(int initialCapacity, Comparator<? super E> comparator) { if (initialCapacity < 1) throw new IllegalArgumentException(); this.lock = new ReentrantLock(); this.notEmpty = lock.newCondition(); this.comparator = comparator; this.queue = new Object[initialCapacity]; } public PriorityBlockingQueue(Collection<? extends E> c) {...} ... }
它的构造函数和PriorityQueue
基本一样,因此使用方式其实也是差不多的。只是它增加了对多线程的处理、以及对阻塞队列特性的支持~~~~
Demo Show:
public static void main(String[] args) { // 注意的是:它没有提供和PriorityQueue一样的只提供比较器的构造函数,我个人觉得是JDK忘了~~~~ PriorityBlockingQueue<String> priorityQueue = new PriorityBlockingQueue<>(11,Comparator.reverseOrder()); priorityQueue.add("orange"); priorityQueue.add("fig"); priorityQueue.add("watermelon"); priorityQueue.add("lemon"); while (priorityQueue.size() != 0) { System.out.println(priorityQueue.remove()); } }
输出:
watermelon orange lemon fig
可以看出,使用方式几乎一样~~~
总结
(阻塞)队列能解决我们最常规的排队的问题,而优先级队这种数据结构能够解决我们业务中可能会用到的VIP排队问题。
比如有个常规的使用场景:优先级消息(MQ消息),有的就是基于JDK的优先级队列来实现的~~~