我的用例需要Stack
数据结构。我应该能够将项目推送到数据结构中,我只想从堆栈中检索最后一项。 JavaDoc for Stack说:
一套更完整,更一致的LIFO堆栈操作 由Deque接口及其实现提供,应该 优先使用此类。例如:
Deque<Integer> stack = new ArrayDeque<>();
我绝对不希望在这里使用同步行为,因为我将在方法本地使用此数据结构。除此之外,为什么我更喜欢Deque
而不是Stack
?
P.S:Deque的javadoc说:
Deques也可以用作LIFO(后进先出)堆栈。这个 接口应该优先于传统的Stack类使用。
答案 0 :(得分:155)
首先,它在继承方面更为明智。在我看来,Stack
扩展Vector
的事实真的很奇怪。早在Java中,继承被过度使用IMO - Properties
是另一个例子。
对我而言,您引用的文档中的关键词是一致。 Deque
公开了一组操作,这些操作都是关于能够从集合的开头或结尾获取/添加/删除项目,迭代等等 - 就是这样。故意无法按位置访问元素,Stack
公开,因为它是Vector
的子类。
哦,而且Stack
也没有界面,所以如果你知道你需要Stack
个操作,你最终会投入到具体的具体课程中,这通常不是一个好主意。
正如评论中指出的那样,Stack
和Deque
具有反向迭代顺序:
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是您想要从头部和尾部检索元素的情况。如果你想要一个简单的堆栈,就没有必要去一个双端队列。