我为什么要使用Deque over Stack?

Gee*_*eek 134 java data-structures

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

Deque接口及其实现提供了更完整和一致的LIFO堆栈操作集,应优先使用此类.例如:

Deque<Integer> stack = new ArrayDeque<>();
Run Code Online (Sandbox Code Playgroud)

我肯定不希望在这里同步行为,因为我将使用本地数据结构的方法.除了这个,我为什么要喜欢DequeStack这里?

PS:Deque的javadoc说:

Deques也可以用作LIFO(后进先出)堆栈.应优先使用此接口,而不是传统的Stack类.

Jon*_*eet 166

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

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

哦,也Stack没有界面,所以如果你知道你需要Stack操作,你最终会承诺一个特定的具体课程,这通常不是一个好主意.

  • @Geek:你没有.关键是如果你想要排队行为,你可以*使用`LinkedList`,但`ArrayDequeue`会(通常)更快.如果你想要堆栈行为,你可以*使用`Stack`但是`ArrayDeque`会(通常)更快. (21认同)
  • ArrayDeque的javadoc说:"当用作堆栈时,这个类可能比Stack快,而当用作队列时,它比LinkedList更快." ..如何指定是否打算将其用作堆栈或队列? (8认同)
  • 但是在抽象方面不是不太合理吗?我的意思是,在抽象方面,两种解决方案都不是很好,因为`Stack`有rep曝光问题,但如果我想要一个堆栈数据结构,我希望能够调用push,pop和peek之类的方法,而不是与堆栈另一端有关的事情. (7认同)
  • @JonSkeet也是Stack的迭代器是错误的,因为它从下到上而不是从上到下迭代.http://stackoverflow.com/questions/16992758/is-there-a-bug-in-java-util-stacks-iterator/27491697#27491697 (3认同)
  • @PeteyPabPro:你说得对。使用 Dequeue 作为堆栈仍然允许非 LIFO 使用作为 Stack 的继承 Vector 方法。问题的解释和解决方案(带封装)可以在这里找到:http://baddotrobot.com/blog/2013/01/10/stack-vs-deque/ (2认同)
  • @SteveLivingston:我在评论的第一部分(但不是第二部分)中输入了一个拼写错误 - 它是“ArrayDeque”,而不是“ArrayDequeue”。但它肯定存在:https://docs.oracle.com/javase/10/docs/api/java/util/ArrayDeque.html (2认同)
  • @roghayehhosseini:那么a)之前就值得这么说;b) 它使它更适合出现在一个新问题中,而不是作为对近 8 年前的答案的评论……特别是因为这意味着答案已被破坏,但事实并非如此。 (2认同)

小智 15

以下是 Deque 优于 Stack 的几个原因:

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

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

性能:Stack 扩展的 Vector 类基本上是 ArrayList 的“线程安全”版本。同步可能会对您的应用程序造成显着的性能影响。此外,使用不需要的功能(如#2 中提到的)扩展其他类会使您的对象膨胀,可能会花费大量额外的内存和性能开销。

  • 这是一个很好的、利基的解释。毫无疑问是简短的采访材料。谢谢。 (4认同)

Ame*_*bsa 7

使用Dequeover 的另一个原因StackDeque能够使用流转换为列表,同时保留 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 = stack.stream().collect(Collectors.toList());//[1,2]

List<Integer> list2 = deque.stream().collect(Collectors.toList());//[2,1]
Run Code Online (Sandbox Code Playgroud)

  • 很好,谢谢你的例子。 (2认同)

iru*_*yak 5

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

如果您查看此处的通用实现- 您会看到有一种一致的方法来实现 set、map 和 list。

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

  • 我们可以使用首选的声明风格,例如在此处Set<String> set = new HashSet<String>();查看原因。

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

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