我为什么要使用Deque over Stack?

时间:2012-09-21 05:38:05

标签: java data-structures

我的用例需要Stack数据结构。我应该能够将项目推送到数据结构中,我只想从堆栈中检索最后一项。 JavaDoc for Stack说:

  

一套更完整,更一致的LIFO堆栈操作   由Deque接口及其实现提供,应该   优先使用此类。例如:

Deque<Integer> stack = new ArrayDeque<>();

我绝对不希望在这里使用同步行为,因为我将在方法本地使用此数据结构。除此之外,为什么我更喜欢Deque而不是Stack

P.S:Deque的javadoc说:

  

Deques也可以用作LIFO(后进先出)堆栈。这个   接口应该优先于传统的Stack类使用。

8 个答案:

答案 0 :(得分:155)

首先,它在继承方面更为明智。在我看来,Stack扩展Vector的事实真的很奇怪。早在Java中,继承被过度使用IMO - Properties是另一个例子。

对我而言,您引用的文档中的关键词是一致Deque公开了一组操作,这些操作都是关于能够从集合的开头或结尾获取/添加/删除项目,迭代等等 - 就是这样。故意无法按位置访问元素,Stack公开,因为它是Vector的子类。

哦,而且Stack也没有界面,所以如果你知道你需要Stack个操作,你最终会投入到具体的具体课程中,这通常不是一个好主意。

正如评论中指出的那样,StackDeque具有反向迭代顺序:

Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println(new ArrayList<>(stack)); // prints 1, 2, 3


Deque<Integer> deque = new ArrayDeque<>();
deque.push(1);
deque.push(2);
deque.push(3);
System.out.println(new ArrayList<>(deque)); // prints 3, 2, 1

也在Deque.iterator()的JavaDocs中解释:

  

以适当的顺序返回此双端队列中元素的迭代器。元素将按照从第一个(头部)到最后一个(尾部)的顺序返回。

答案 1 :(得分:7)

以下是Deque优于Stack的一些原因:

面向对象的设计-继承,抽象,类和接口:堆栈是一个类,而双端队列是一个接口。 Java只能扩展一个类,而Java中的单个类可以实现任意数量的接口(类型的多重继承)。使用Deque接口可消除对具体Stack类及其祖先的依赖,并为您提供更大的灵活性,例如扩展不同类或交换出Deque的不同实现的自由(如LinkedList,ArrayDeque)。

不一致:堆栈扩展了Vector类,该类允许您按索引访问元素。这与Stack实际应该执行的操作不一致,这就是为什么首选Deque接口(不允许此类操作)的原因-Deque接口允许的操作与FIFO或LIFO数据结构应允许的操作一致。

性能:Stack扩展的Vector类基本上是ArrayList的“线程安全”版本。同步可能会严重影响您的应用程序性能。另外,扩展具有不必要功能的其他类(如#2中所述)会使对象膨胀,从而可能会花费大量额外的内存和性能开销。

答案 2 :(得分:3)

以下是我对Stack类描述中提到的不一致的解释。

如果您查看通用实现here - 您将看到有一致的方法来实现集合,映射和列表。

  • 对于set和map,我们有2个带有哈希映射和树的标准实现。第一个是最常用的,第二个是在我们需要一个有序结构时使用的(它还实现了自己的接口 - SortedSet或SortedMap)。

  • 我们可能会使用首选的声明式Set<String> set = new HashSet<String>();来查看原因here

但Stack类:1)没有自己的接口; 2)是Vector类的子类 - 它基于可调整大小的数组;那么堆栈的链表实现在哪里?

在Deque接口中,我们没有这样的问题,包括两个实现(可调整大小的数组 - ArrayDeque;链表 - LinkedList)。

答案 3 :(得分:2)

使用Dequeue而不是堆栈的另一个原因是Dequeue能够使用流转换为列表的流,并保持应用LIFO概念,而没有使用堆栈。

Stack<Integer> stack = new Stack<>();
Deque<Integer> deque = new ArrayDeque<>();

stack.push(1);//1 is the top
deque.push(1)//1 is the top
stack.push(2);//2 is the top
deque.push(2);//2 is the top

List<Integer> list1 = strack.stream().collect(Collectors.toList());//[1,2]
List<Integer> list2 = deque.stream().collect(Collectors.toList());//[2,1]

答案 4 :(得分:1)

性能原因

我们已经看到 Stack 类是 java.util.Vector 的子类。 Vector 类是同步的。它使用传统的方式来实现线程安全:使其方法“同步”

作为其子类,Stack 类也是同步的

另一方面,Deque 接口不是线程安全的

因此,如果不需要线程安全,Deque 可以为我们带来比 Stack 更好的性能

答案 5 :(得分:0)

对我来说,这一点很重要: 堆栈是线程安全的,因为它是从Vector派生的,而大多数双端队列的实现则不是,因此如果只在单个线程中使用它,则速度会更快。

答案 6 :(得分:0)

性能可能是一个原因。通过使用Deque替换Stack,我使用的算法从7.6分钟缩短到1.5分钟。

答案 7 :(得分:-4)

使用Deque是您想要从头部和尾部检索元素的情况。如果你想要一个简单的堆栈,就没有必要去一个双端队列。