Java流是连续阶段的吗?

时间:2017-07-01 15:20:29

标签: java lambda java-8 java-stream

我对中间阶段顺序状态有疑问 - 从应用于所有的阶段的操作输入流(项目)是所有阶段/操作应用于每个流项?

我知道这个问题可能不容易理解,所以我举一个例子。在以下流处理中:

List<String> strings = Arrays.asList("Are Java streams intermediate stages sequential?".split(" "));
strings.stream()
           .filter(word -> word.length() > 4)
           .peek(word -> System.out.println("f: " + word))
           .map(word -> word.length())
           .peek(length -> System.out.println("m: " + length))
           .forEach(length -> System.out.println("-> " + length + "\n"));

我对这段代码的期望是输出:

f: streams
f: intermediate
f: stages
f: sequential?

m: 7
m: 12
m: 6
m: 11

-> 7
-> 12
-> 6
-> 11

相反,输出是:

f: streams
m: 7
-> 7

f: intermediate
m: 12
-> 12

f: stages
m: 6
-> 6

f: sequential?
m: 11
-> 11

由于控制台输出,所有阶段的项目是否只显示 ?或者他们也为所有阶段处理,一次一个?

如果问题不够明确,我可以进一步详细说明这个问题。

4 个答案:

答案 0 :(得分:18)

此行为启用代码的优化。如果每个中间操作都要在进行下一个中间操作之前处理流的所有元素,那么就不会有优化

所以为了回答你的问题,每个元素一次一个地垂直移动流管道(除了稍后讨论的一些有状态操作),因此尽可能地进行优化。

解释

根据您提供的示例,由于不包含有状态操作,每个元素将逐个沿着流管道垂直移动。

另一个例子,假设您正在寻找长度大于String的第一个4,在提供结果之前处理所有元素是不必要且耗时的。

考虑这个简单的例子:

List<String> stringsList = Arrays.asList("1","12","123","1234","12345","123456","1234567");
int result = stringsList.stream()
                        .filter(s -> s.length() > 4)
                        .mapToInt(Integer::valueOf)
                        .findFirst().orElse(0);

上面的filter中间操作将找到长度大于4的所有元素并返回它们的新流但是会发生什么我们找到长度大于4的第一个元素,该元素经过.mapToInt,然后findFirst说“我找到了第一个元素”,执行就在那里停止。因此,结果将是12345

有状态和无状态中间操作的行为

请注意,当流管道中包含sorted之类的有状态中间操作时,特定操作将遍历整个流。如果你仔细想想,这就完全合情合理,因为你需要对元素进行排序,以确定哪些元素在排序顺序中排在第一位。

distinct中间操作也是一个有状态操作,但是,正如@Holger提到的不同于sorted,它不需要遍历整个流,因为每个不同的元素都可以立即传递给管道并且可能会遇到短路情况。

无状态中间操作(如filtermap等不必遍历整个流,并且可以如上所述垂直地一次处理一个元素。

最后,但同样重要的是要注意,当终端操作是短路操作时,终端短路方法可以在遍历底层流的所有元素之前完成。

阅读: Java 8 stream tutorial

答案 1 :(得分:4)

  

您的答案是loop fusion。我们看到的是四个   中间操作filter() – peek() – map() – peek() – println using forEach() which is a kinda terminal operation一直是逻辑上的   联合起来构成单程。 他们被执行   订购每个元素。这个加入   单次操作中的操作一起是一种优化技术   称为loop fusion

阅读更多内容:Source

答案 2 :(得分:2)

中间操作总是懒惰地执行。也就是说  它们直到达到终端操作才运行。   流中使用的一些最流行的中间操作

filter – the filter operation returns a stream of elements that
satisfy the predicate passed in as a parameter to the operation. The
elements themselves before and after the filter will have the same
type, however the number of elements will likely change


map – the map operation returns a stream of elements after they have
been processed by the function passed in as a parameter. The
elements before and after the mapping may have a different type, but
there will be the same total number of elements.

distinct – the distinct operation is a special case of the filter
operation. Distinct returns a stream of elements such that each
element is unique in the stream, based on the equals method of the
elements

java-8-streams-cheat-sheet

答案 3 :(得分:2)

除了optimisation之外,您描述的处理顺序对于不确定长度的流不起作用,如下所示:

DoubleStream.generate(Math::random).filter(d -> d > 0.9).findFirst();

不可否认,这个例子在实践中并没有多大意义,但重点是,DoubleStream.generate()不是由固定大小的集合支持,而是创建一个潜在的无限流。处理它的唯一方法是逐个元素。