Java 8流执行

时间:2018-07-31 19:24:16

标签: java java-8 java-stream

您能解释以下代码的执行过程吗?主要是sorted方法。

Stream.of("d2", "a2", "b1", "b3", "c")
    .sorted((s1, s2) -> {
        System.out.printf("sort: %s; %s\n", s1, s2);
        return s1.compareTo(s2);
    })
    .filter(s -> {
        System.out.println("filter: " + s);
        return s.startsWith("a");
    })
    .map(s -> {
        System.out.println("map: " + s);
        return s.toUpperCase();
    })
    .forEach(s -> System.out.println("forEach: " + s));

输出:

sort:    a2; d2
sort:    b1; a2
sort:    b1; d2
sort:    b1; a2
sort:    b3; b1
sort:    b3; d2
sort:    c; b3
sort:    c; d2
filter:  a2
map:     a2
forEach: A2
filter:  b1
filter:  b3
filter:  c
filter:  d2

谢谢,编号:https://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/

2 个答案:

答案 0 :(得分:1)

因此Java8试图通过对任何给定的输入尽快执行所有高阶函数来加快执行速度。也就是说,例如,如果您在列表上连续​​两次调用map,则它将仅访问列表的每个元素一次。这样可以加快速度,因为它从2次遍历到了1个。为了说明这一点,请举一个简单的例子:

Stream.of(1, 2, 3)
    .map(s -> {
        System.out.println("map: " + s.toString());
        return s;
    })
    .map(s -> {
        System.out.println("map: " + s.toString());
        return s;
    })

这将打印:

1
1
2
2
3
3

因为一次“触摸”列表中的每个元素比对两个map进行完全遍历列表的速度更快!

就您的示例而言,让我们对其进行逐步介绍:

sort:    a2; d2
sort:    b1; a2
sort:    b1; d2
sort:    b1; a2
sort:    b3; b1
sort:    b3; d2
sort:    c; b3
sort:    c; d2

所有排序需要同时发生,并且所有这些都必须首先发生。这是因为在排序完成之前,计算机无法知道哪个元素将位于哪个位置(即,它无法在同一列表位置上进行两次映射,因为排序可能会改变该位置)

接下来,您基本上是这样的:

Stream.of("a2", "b1", "b3", "c", "d2")
    .filter(s -> {
        System.out.println("filter: " + s);
        return s.startsWith("a");
     })
    .map(s -> {
        System.out.println("map: " + s);
        return s.toUpperCase();
    })
    .forEach(s -> System.out.println("forEach: " + s));

现在,为了最小化列表中的传递,Java将遍历列表中的每个元素并执行filter,然后执行map,然后执行forEach。这是因为这些都不依赖于元素的位置。换句话说,Java看到它可以对每个元素执行所有这些操作,而不是对每个函数重复遍历整个列表三遍!

现在:

filter:  a2
map:     a2
forEach: A2

我们filter第一个元素,然后map在它上面,然后进行最后的forEach打印。

filter:  b1
filter:  b3
filter:  c
filter:  d2

所有这些都将被过滤掉,因此其余函数不会被调用!

答案 1 :(得分:0)

您似乎对为什么输出中连续有几个排序的调用感到困惑。

让我们解释一下;首先,将中间操作分为filtermap等的 stateless stateful 等。 sorteddistinct

如Brian Goetz here under Executing a stream pipeline所述:

  

无状态操作是可以对元素执行的操作   没有任何其他要素的知识。例如,一个   过滤操作只需要检查当前元素即可   确定是包括还是消除它,但是排序操作   必须先查看所有元素,然后才能知道首先发出哪个元素

强调我的。

排序操作必须先查看所有元素,然后才能将给定元素发射给下一个操作,这就是为什么在输出中连续看到多个排序调用的原因。


此外,您可能还想阅读are Java streams stages sequential? 它在某种程度上衍生自sorted操作。