1. 队列
1.1 定义
队列(Queue)是一种经常使用的集合。Queue实际上是实现了一个先进先出(FIFO:First In First Out)的有序表。它和List的区别在于,List可以在任意位置添加和删除元素,而Queue只有两个操作:
- 把元素添加到队列末尾;
- 从队列头部取出元素。
超市的收银台就是一个队列:
在Java的标准库中,队列接口Queue定义了以下几个方法:
int size():获取队列长度;boolean add(E)/boolean offer(E):添加元素到队尾;E remove()/E poll():获取队首元素并从队列中删除;E element()/E peek():获取队首元素但并不从队列中删除。
对于具体的实现类,有的Queue有最大队列长度限制,有的Queue没有。注意到添加、删除和获取队列元素总是有两个方法,这是因为在添加或获取元素失败时,这两个方法的行为是不同的。我们用一个表格总结如下:
System.out.println("print the data.");
Queue<Integer> linkQueue = new LinkedList<>();
linkQueue.add(1);
linkQueue.add(2);
linkQueue.add(3);
System.out.println(linkQueue.poll()); // 1
System.out.println(linkQueue.poll()); // 2
System.out.println(linkQueue.poll()); // 3
poll()和peek()为例来说说“获取并删除”与“获取但不删除”的区别
1.2 小结
队列Queue实现了一个先进先出(FIFO)的数据结构:
- 通过
add()/offer()方法将元素添加到队尾; - 通过
remove()/poll()从队首获取元素并删除; - 通过
element()/peek()从队首获取元素但不删除。
要避免把null添加到队列。
2. 优先队列
2.1 定义
我们知道,Queue是一个先进先出(FIFO)的队列。在银行柜台办业务时,我们假设只有一个柜台在办理业务,但是办理业务的人很多,怎么办?
可以每个人先取一个号,例如:A1、A2、A3……然后,按照号码顺序依次办理,实际上这就是一个Queue。
如果这时来了一个VIP客户,他的号码是V1,虽然当前排队的是A10、A11、A12……但是柜台下一个呼叫的客户号码却是V1。
这个时候,我们发现,要实现“VIP插队”的业务,用Queue就不行了,因为Queue会严格按FIFO的原则取出队首元素。我们需要的是优先队列:PriorityQueue。
PriorityQueue和Queue的区别在于,它的出队顺序与元素的优先级有关,对PriorityQueue调用remove()或poll()方法,返回的总是优先级最高的元素。
要使用PriorityQueue,我们就必须给每个元素定义“优先级”。我们以实际代码为例,先看看PriorityQueue的行为:
private void PriorityQueueTest(){
PriorityQueue<Integer> queue = new PriorityQueue<>();
queue.add(5);
queue.add(4);
queue.add(7);
queue.add(9);
queue.add(11);
int size = queue.size();
for(int i=0;i<size;i++){
System.out.println(queue.poll());
}
}
打印:
4
5
7
9
11
2.2 小结
PriorityQueue实现了一个优先队列:从队首获取元素时,总是获取优先级最高的元素。
PriorityQueue默认按元素比较的顺序排序(必须实现Comparable接口),也可以通过Comparator自定义排序算法(元素就不必实现Comparable接口)。
3. 双端队列
3.1 定义
如果把条件放松一下,允许两头都进,两头都出,这种队列叫双端队列(Double Ended Queue),学名Deque。
Java集合提供了接口Deque来实现一个双端队列,它的功能是:
- 既可以添加到队尾,也可以添加到队首;
- 既可以从队首获取,又可以从队尾获取。
我们来比较一下Queue和Deque出队和入队的方法:
对于添加元素到队尾的操作,Queue提供了add()/offer()方法,而Deque提供了addLast()/offerLast()方法。添加元素到对首、取队尾元素的操作在Queue中不存在,在Deque中由addFirst()/removeLast()等方法提供。
注意到Deque接口实际上扩展自Queue:
public interface Deque<E> extends Queue<E> {
...
}
因此,Queue提供的add()/offer()方法在Deque中也可以使用,但是,使用Deque,最好不要调用offer(),而是调用offerLast():
Deque<String> deque = new LinkedList<>();
deque.offerLast("A"); // A
deque.offerLast("B"); // A <- B
deque.offerFirst("C"); // C <- A <- B
System.out.println(deque.pollFirst()); // C, 剩下A <- B
System.out.println(deque.pollLast()); // B, 剩下A
System.out.println(deque.pollFirst()); // A
System.out.println(deque.pollFirst()); // null
3.2 小结
Deque实现了一个双端队列(Double Ended Queue),它可以:
- 将元素添加到队尾或队首:
addLast()/offerLast()/addFirst()/offerFirst(); - 从队首/队尾获取元素并删除:
removeFirst()/pollFirst()/removeLast()/pollLast(); - 从队首/队尾获取元素但不删除:
getFirst()/peekFirst()/getLast()/peekLast(); - 总是调用
xxxFirst()/xxxLast()以便与Queue的方法区分开; - 避免把
null添加到队列。