如何使用 Java 8 Stream 实现 Stack Iteration

How to implement Stack Iteration using Java 8 Stream

我有一个 Stack<Object> 和以下代码:

while(!stack.isEmpty()){
    Object object = stack.pop();
    // do some operation on object
}

如何使用 Java 8 Stream 实现此迭代,以便它循环直到堆栈为空,并且在每次迭代中都应通过从顶部弹出一个元素来减少堆栈?

不可能使用堆栈流,因为首先它是先进先出的顺序,其次是因为它基于迭代器会抛出 ConcurrentModificationException。仍然可能,但与简单的 for 循环相比当然不推荐:

IntStream.range(0, s.size()).forEach(i -> stack.pop());

在 Java 9 中,将有一个 Stream.iterate 的 3-arg 版本(类似于 for 循环——初始值,用于确定输入结束的 lambda, lambda 用于确定下一个输入)可以做到这一点,虽然它会有点紧张:

if (!stack.isEmpty()) {
    Stream.iterate(stack.pop(), 
                   e -> !stack.isEmpty(), 
                   e -> stack.pop())
          ...
}

如果您不想等待 ,这里有一个在 Java8 下工作的流工厂。

public static <T> Stream<T> pop(Stack<T> stack) {
    return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(
        stack.size(), Spliterator.ORDERED|Spliterator.SIZED) {
            public boolean tryAdvance(Consumer<? super T> action) {
                if(stack.isEmpty()) return false;
                action.accept(stack.pop());
                return true;
            }
    }, false);
}

请注意,这会报告堆栈的初始大小,这是理所当然的,这意味着您不能在中间更改堆栈(无论如何在中间修改流源都是一个坏主意)。另一方面,这将使某些流操作比迭代变体更有效。

现在,适用于两种变体的一般警告。由于正在进行的 Stream 操作(如弹出 Stream 使用的元素)而修改的流源可能会使源处于不可预测的状态。短路操作可能不会消耗所有元素,并且结合并行流,它们仍然可能消耗比终端操作所需更多的元素。

非常类似于BufferedReader.lines()

After execution of the terminal stream operation there are no guarantees that the reader will be at a specific position from which to read the next character or line.

你不应该对以这种方式消耗元素后的 Stack 内容做出任何假设。