如果把条件放松一下,允许两头都进,两头都出,这种队列叫双端队列(Double Ended Queue),学名Deque
。
Java集合提供了接口Deque
来实现一个双端队列,它的功能是:
我们来比较一下Queue
和Deque
出队和入队的方法:
Queue | Deque | |
---|---|---|
添加元素到队尾 | add(E e) / offer(E e) | addLast(E e) / offerLast(E e) |
取队首元素并删除 | E remove() / E poll() | E removeFirst() / E pollFirst() |
取队首元素但不删除 | E element() / E peek() | E getFirst() / E peekFirst() |
添加元素到队首 | 无 | addFirst(E e) / offerFirst(E e) |
取队尾元素并删除 | 无 | E removeLast() / E pollLast() |
取队尾元素但不删除 | 无 | E getLast() / E peekLast() |
注意到Deque
接口实际上扩展自Queue
:
public interface Deque<E> extends Queue<E> { ... }
因此,Queue
提供的add()
/offer()
方法在Deque
中也可以使用,但是,使用Deque
,最好不要调用offer()
,而是调用offerLast()
:
联想:按照 C++ 思维,那么 Quene 应该作为适配器来封装 Deque,但是,按照 JAVA 的思维,接口和实现分开,那么 Deque 是较 Quene 更为丰富的接口。
import java.util.Deque; import java.util.LinkedList; public class Main { public static void main(String[] args) { 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 } }
使用Deque
,推荐总是明确调用offerLast()
/offerFirst()
或者pollFirst()
/pollLast()
方法。
Deque
是一个接口,它的实现类有ArrayDeque
和LinkedList
。
我们发现LinkedList
真是一个全能选手,它即是List
,又是Queue
,还是Deque
。但是我们在使用的时候,总是用特定的接口来引用它,这是因为持有接口说明代码的抽象层次更高,而且接口本身定义的方法代表了特定的用途。
// 不推荐的写法: LinkedList<String> d1 = new LinkedList<>(); d1.offerLast("z"); // 推荐的写法: Deque<String> d2 = new LinkedList<>(); d2.offerLast("z");
可见面向抽象编程的一个原则就是:尽量持有接口,而不是具体的实现类。
联想:C++ 中是有具体类不用管底层实现,当然你可以自己通过类模板参数来指定底层容器。而JAVA需要你明确接口并且给接口找到一个具体实现。