28

我有一个方法,每次执行时都会生成一个对象,我需要颠倒获取它们的顺序。所以我认为这样做的自然方法是堆栈,因为它是 LIFO。

但是,Java Stack似乎不能很好地与新的 Java 8 流式 API 配合使用。

如果我这样做:

   Stack<String> stack = new Stack<String>();
   stack.push("A");
   stack.push("B");
   stack.push("C");

   List<String> list = stack.stream().collect(Collectors.toList());
   System.out.println("Collected: " + list);

我得到的输出是:

Collected: [A, B, C]

为什么不按预期的 LIFO 顺序将它们输出到流中?这是以正确(LIFO)顺序将所有项目从堆栈中清除到列表中的正确方法吗?

4

1 回答 1

50

正如评论中已经提到的,我们有经过良好测试的Deque界面,应该是首选。

但我会告诉你为什么Stack不应该使用的原因。

首先,Java Doc。堆栈的自己说:

Deque 接口及其实现提供了一组更完整和一致的 LIFO 堆栈操作,应优先使用此类。例如:

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

请参阅JavaDoc

那么Stack班级的问题是什么。

就像 Martin Fowler 在他的书Refactoring: Improvement the Design of Existing Code中已经提到 的重构方法Replace Inheritance with Delegation一样,Stack 不应该从 Vector 继承。

不恰当继承的典型例子之一是使堆栈成为向量的子类。Java 1.1 在其实用程序中做到了这一点(顽皮的男孩!)[6, p. 288]

相反,他们应该使用如下图所示的委托,这也来自书中。

参见这里:用委托替换继承

用委托代替继承

那么,为什么这是一个问题:

因为堆栈只有 5 个方法:

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

size()并且isEmpty()是从类继承的,并且不使用Vector其他方法。Vector但是通过继承,其他方法被转发给Stack没有意义的类。

福勒对这个问题说:

您可以接受这种情况并使用约定说虽然它是一个子类,但它只使用了超类函数的一部分。但这会导致代码在您的意图是另一件事时说一件事——您应该消除这种混乱。

这损害了接口隔离原则

其中说:

不应强迫客户依赖他们不使用的接口。


您可以查看VectorStack类的源代码,您会看到 Stack 类继承了该类的spliterator方法和VectorSpliteratorinnerClass Vector

这个方法被Collection接口用来实现。流方法的默认版本:

default Stream<E> stream() {
  return StreamSupport.stream(spliterator(), false);
}

因此,请避免简单地使用VectorandStack类。

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

于 2015-05-22T06:36:31.827 回答