使用Java 8集合流API进行堆栈
我有一个方法,每次执行它时都会生成一个对象,我需要颠倒我获取它们的顺序。 所以我认为这样做的自然方式是Stack,因为它是LIFO。
但是,Java Stack似乎不能与新的Java 8流API一起使用。
如果我这样做:
Stack stack = new Stack(); stack.push("A"); stack.push("B"); stack.push("C"); List list = stack.stream().collect(Collectors.toList()); System.out.println("Collected: " + list);
我得到的输出是:
Collected: [A, B, C]
为什么不以预期的LIFO顺序将它们输出到流中? 这是将所有项目从堆栈清除到右(LIFO)订单列表的正确方法吗?
正如评论中已经提到的,我们已经对Deque
接口进行了很好的测试,这应该是首选。
但是我会告诉你不应该使用Stack
的原因。
首先,Java Doc。 堆栈说自己:
Deque接口及其实现提供了更完整和一致的LIFO堆栈操作集,应优先使用此类。 例如:
Deque stack = new ArrayDeque();
请参阅JavaDoc 。
那么Stack
类的问题是什么呢。
就像Martin Fowler在他的“ Refactoring:改进现有代码的设计”一书中提到的那样,在重构方法中将inheritance替换为委托 ,Stack不应该从Vectorinheritance。
不适当inheritance的一个典型例子是使堆栈成为vector的子类。 Java 1.1在它的实用程序(顽皮的男孩!)中做到这一点[6,p。 288]
相反,他们应该使用如下图所示的委托,这也来自书中。
另请参见: 使用委派替换inheritance
那么为什么这是一个问题:
因为Stack只有5个方法:
- 流行的
- 推
- 是空的
- 搜索
-
尺寸
size()
和isEmpty()
inheritance自Vector
类,而不使用Vector
中的其他方法。 但是通过inheritance,其他方法被转发到Stack
类,这没有任何意义。
福勒对这个问题说:
你可以忍受这种情况并使用约定来说虽然它是一个子类,但它只使用了超类函数的一部分。 但这会导致代码在你的意图是其他东西时说出一件事 – 你应该删除的混乱。
这会损害接口隔离原则
其中说:
客户不应强制依赖他们不使用的接口。
您可以查看 Vector和Stack类的源代码 ,您将看到Stack类inheritance了Vector
类中的spliterator
方法和VectorSpliterator
innerClass。
Collection
接口使用此方法进行impl。 stream方法的默认版本:
default Stream More ...stream() { return StreamSupport.stream(spliterator(), false); }
因此,请避免使用Vector
和Stack
类。
[6]重构:改进现有代码福勒的设计,Martin 1997年