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

一个更完整和一致的后进先出堆栈操作集是 由Deque接口及其实现提供,这应该 优先用于该类。例如:

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

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

附:Deque的javadoc说:

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


一方面,它在继承方面更合理。在我看来,Stack扩展Vector的事实真的很奇怪。在Java早期,继承被过度使用IMO—属性是另一个例子。

对我来说,你引用的文件中的关键字是一致的。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

这在JavaDocs for Deque.iterator()中也有解释:

以适当的顺序返回该deque中元素的迭代器。元素将按照从第一个(头)到最后一个(尾)的顺序返回。


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

如果你看一下这里的通用实现,你会发现set、map和list的实现方法是一致的。

对于set和map,我们有两个标准的哈希映射和树实现。第一个是最常用的,第二个是当我们需要一个有序结构时使用的(它也实现了自己的接口——SortedSet或SortedMap)。 我们可以使用首选的样式,如Set<String> Set = new HashSet<String>();原因见这里。

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

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


对我来说,这一点是缺失的: Stack是线程安全的,因为它是从Vector派生的,而大多数deque实现不是,因此如果你只在单个线程中使用它,速度会更快。


使用Deque而不是Stack的另一个原因是Deque有能力使用流转换为列表,同时保持LIFO概念的应用,而Stack没有。

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]

业绩可能是原因之一。我使用的一种算法通过将Stack替换为Deque将时间从7.6分钟缩短到1.5分钟。


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

Object oriented design - Inheritance, abstraction, classes and interfaces: Stack is a class, Deque is an interface. Only one class can be extended, whereas any number of interfaces can be implemented by a single class in Java (multiple inheritance of type). Using the Deque interface removes the dependency on the concrete Stack class and its ancestors and gives you more flexibility, e.g. the freedom to extend a different class or swap out different implementations of Deque (like LinkedList, ArrayDeque).

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

性能:Stack扩展的Vector类基本上是ArrayList的“线程安全”版本。同步可能会对应用程序造成严重的性能影响。此外,扩展其他类使用不需要的功能(如第2条所述)会使对象膨胀,可能会消耗大量额外的内存和性能开销。


Deque在实现方面优于stack有以下几个原因:

Deque is an interface and stack is a class: In class creation it is better to implement an interface than extend a class because after extending you cannot extend another class, you can only implement an interface in the other hand when you implement an interface you can extend a class and also implement another interfaces. Synchronization: Because stack class is a subclass of the vector class which is asynchronized therefor stack is too But Deque is not. So if there is no need for synchronization then for better performance we should use Deque. Deque‘s iterator works the way we expect for a stack: iteration in a stack is bottom to top (FIFO (First In First Out)). But iteration in a Deque is top to bottom (LIFO (Last In First Out)). Stack isn't truly LIFO: We know that stack is a subclass of the vector class so we can access to elements by their indexes which is against LIFO contract.


如果出于某种原因你想从Stack转换到Deque,但想在转换到ArrayList时保持相同的顺序,你可以使用Deque. descent iterator()。

但是ArrayList没有接受Iterator的构造函数,所以你可能想把它和一个库结合起来,比如Guava(或Apache Commons):

Lists.newArrayList(deque.descendingIterator()) // Guava

或者Java 8:

List<Integer> list = new ArrayList<>();
deque.descendingIterator().forEachRemaining(list::add);