Java 8 Streams API 和 Stack 类

3

我希望了解在Java 8流API中使用Stack类的行为。

Stack<Integer> s = new Stack<>();

s.push(1);
s.push(2);
s.push(3);
s.push(4);

s.stream().forEach( x-> System.out.print(x + " "));

根据 Stack 类的合同,这段代码应该打印出 4 3 2 1

但实际上,它打印出了 1 2 3 4

基本上我想要了解:

  1. 导致这种行为的底层低级实现细节。

  2. 在使用 Stream API 迭代有序集合时,是否还存在其他已知的问题。

  3. 如果我想要实现自己的 Stack 类,并与 Java 8 Streams 兼容,需要做哪些更改?


1
根据Stack类的契约,只有在弹出内容时(除非堆栈为空),此代码才应打印4 3 2 1。 - Naman
a) "Stack类的契约的哪一部分让你认为它应该打印4 3 2 1?请引用文档。" b) 文档明确指出:“Deque接口及其实现应优先使用,而不是使用此类。”- 为什么您要使用过时的类? - Erwin Bolwidt
1
“Stack” 使用 “Vector”的迭代器,该迭代器基于光标,起始位置设置为“0”。它们的 “Iterator#next” 基本上返回 elementData[cursor++],其中 elementData 是一个普通的 Object[] 数组,由两个类用于存储元素。 - Andrew Tobilko
采纳答案。感谢提醒。 - Amit Jaiswal
非常欢迎您的到来。但请不要告诉任何人。我被告知提醒别人是A) 不必要的,B) 是完全错误的;-) ... 非常感谢您快速的回复。请注意:当您想直接“回应”某个人时,请使用@用户名,以便让该用户收到有关消息的通知。 - GhostCat
显示剩余3条评论
3个回答

5
误解:`stream()` 方法来源于 `Collection` 接口。
而它的 JavaDoc 告诉我们:
“返回以此集合为源的顺序流。”
换句话说: “Stream 协议”不知道也不关心“ Stack 协议”。
除此之外,“ Stack协议 ”是关于“堆栈操作”的。换句话说: 通过流迭代 Stack 时,不存在关于元素排序的保证。
而且,Stack 本身的 JavaDoc 告诉我们:
“Stack 类表示对象的后进先出(LIFO)堆栈。它使用五个操作扩展了 Vector 类,允许将向量视为堆栈。”
所以,你理解得没问题: Stack 只是一个提供了“Stack行为”的向量(列表),没有其他任何操作。你可以在这里看到:
for (Integer i : s) {
    System.out.println(i);
  }

所以,即使只是迭代你的堆栈而略过了流式部分,它也会按插入顺序打印元素1、2、3、4。

5

Stack类已经过时(实际上早于集合框架API),你应该使用Deque。正如Deque javadoc所述,Deques也可以用作LIFO(后进先出)堆栈。应该优先使用此接口而不是传统的Stack。就像这样,

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

这将改变输出结果为

4 3 2 1 

好的回答。谢谢。 - Naman
1
这是一个很好的回答,但没有回答OP的任何问题(共有3个问题)。 - Andrew Tobilko
1
@AndrewTobilko,这句话在字里行间都表达了。它基本上是在说:“不要浪费时间使用堆栈,使用双端队列”;-) - GhostCat

2
it prints 1 2 3 4.

原因

Stack 类扩展自 Vector - 保持了原始插入顺序,pop 方法返回 Stack 中的最后一个对象。事实上,我认为在调用 stream() 方法时打破直觉顺序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接