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

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

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

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

附:Deque的javadoc说:

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


当前回答

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

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

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

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

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

其他回答

一方面,它在继承方面更合理。在我看来,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中元素的迭代器。元素将按照从第一个(头)到最后一个(尾)的顺序返回。

使用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,但想在转换到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);

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

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

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

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

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

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