使用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个方法:

  1. 流行的
  2. 是空的
  3. 搜索
  4. 尺寸

    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); } 

因此,请避免使用VectorStack类。

[6]重构:改进现有代码福勒的设计,Martin 1997年