1. 定义
双向队列:支持插入删除元素的线性集合;java官方文档推荐用deque实现栈(stack)。
2. 和Queue的区别
Deque是double ended queue,将其理解成双端结束的队列,双端队列,可以在首尾插入或删除元素。
Queue的解释中,Queue就是简单的FIFO队列;所以在概念上来说,Queue是FIFO的单端队列,Deque是双端队列。
Queue:只能一端进,并且在另一端出的队列。
Deque:在队列的两端都能进出的队列,继承自Queue接口,Deque的实现类是LinkedList、ArrayDeque、LinkedBlockingDeque,其中LinkedList是最常用的。
3. 继承关系
4. 被弃用的Stack
4.1 被弃用的原因
从继承关系中,我们可以看到Stack的基本方法与底层实现,由于Vector是动态数组接口,其底层的实现是数组,因此,Stack的底层实现也是数组,且继承了Vector的公共方法。
Vector类具有动态扩容和随机访问的特性,因此,继承了Vector类的Stack也同样具有这些特性,这恰好违背了Stack数据结构的设计原理,正因为如此,Java中的Stack一直被认为是糟糕的实现,官方也将Stack标志为“弃用”(deprecated)。
Vector不行是因为效率不太行,很多方法都用了synchronized修饰,虽然线程安全,但是像ArrayDeque,LinkedList这些线程不安全的,在需要安全的时候也可以用Collections.synchronizedCollection()转化成线程安全的,所以Vector就没什么用处了。
综上所述,导致Stack糟糕实现的原因是Stack与Vector类的关系出现了错误,不应该是继承关系(is-a),而应是组合关系(has-a)。
4.1.1 简述Vector
关于Vector,参考:简述Vector_Archie敲键盘的博客-CSDN博客_vector 类 继承
5. 特点
1.插入、删除、获取操作支持两种形式:快速失败和返回null或true/false
2.既具有FIFO特点又具有LIFO特点,即是队列又是栈
3.不推荐插入null元素,null作为特定返回值表示队列为空
4.未定义基于元素相等的equals和hashCode
6. 实现
java官方文档推荐用deque实现栈(stack)。
ArrayDeque: 基于数组实现的线性双向队列,通常作为栈或队列使用,但是栈的效率不如LinkedList高。
LinkedList: 基于链表实现的链式双向队列,通常作为栈或队列使用,但是队列的效率不如ArrayQueue高。
模拟队列——FIFO
Deque接口扩展(继承)了 Queue 接口。在将双端队列用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。从 Queue 接口继承的方法完全等效于 Deque 方法,如下表所示:
模拟栈——LIFO
双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack 类。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于 Deque 方法,如下表所示:
7. ArrayDeque与LinkList区别:
- 数组结构
- 插入元素不能为null
- 无法确定数据量时,后期扩容会影响效率
- 链表结构
- 插入元素能为null
- 无法确定数据量时,有更好表现
ArrayDeque与LinkedList中插入null的差异:
最近做题时发现用ArrayDeque类插入null时会报NullPointerException的错误,而用LinkedList类则不会。查了下两个类实现offer方法的源码,发现ArrayDeque类里是对offer这个方法特意进行了是否为null的判断
而LinkedList类则没有进行这个判断
8. 常用方法
addFirst(): 向队头插入元素,如果元素为空,则发生NPE(空指针异常)
addLast(): 向队尾插入元素,如果为空,则发生NPE
offerFirst(): 向队头插入元素,如果插入成功返回true,否则返回false
offerLast(): 向队尾插入元素,如果插入成功返回true,否则返回false
removeFirst(): 返回并移除队头元素,如果该元素是null,则发生NoSuchElementException
removeLast(): 返回并移除队尾元素,如果该元素是null,则发生NoSuchElementException
pollFirst(): 返回并移除队头元素,如果队列无元素,则返回null
pollLast(): 返回并移除队尾元素,如果队列无元素,则返回null
getFirst(): 获取队头元素但不移除,如果队列无元素,则发生NoSuchElementException
getLast(): 获取队尾元素但不移除,如果队列无元素,则发生NoSuchElementException
peekFirst(): 获取队头元素但不移除,如果队列无元素,则返回null
peekLast(): 获取队尾元素但不移除,如果队列无元素,则返回null
pop(): 弹出栈中元素,也就是返回并移除队头元素,等价于removeFirst(),如果队列无元素,则发生NoSuchElementException
push(): 向栈中压入元素,也就是向队头增加元素,等价于addFirst(),如果元素为null,则发生NPE,如果栈空间受到限制,则发生IllegalStateException
9. 代码演示
private static void usingAsQueue() {Deque<Integer> queue=new ArrayDeque<>();System.out.println("队列为空:"+queue.isEmpty()); //判断队列是否为空queue.addLast(12); //添加元素System.out.println("队列为空:"+queue.isEmpty()); //判断队列是否为空System.out.println(queue.peekFirst()); //获取队列首部元素System.out.println(queue.pollFirst()); //获取并移除栈顶元素System.out.println("队列为空:"+queue.isEmpty()); //判断队列是否为空}private static void usingAsStack() {//作为栈使用Deque<Integer> stack=new LinkedList<>();System.out.println("栈为空:"+stack.isEmpty()); //判断栈是否为空stack.addFirst(12);System.out.println("栈为空:"+stack.isEmpty()); //判断栈是否为空System.out.println(stack.peekFirst()); //获取栈顶元素System.out.println(stack.pollFirst()); //获取并移除栈顶元素System.out.println("栈为空:"+stack.isEmpty()); //判断栈是否为空System.out.println("============================================");