【问题标题】:Java 8 stream computation on whole list整个列表上的 Java 8 流计算
【发布时间】:2019-12-07 10:31:03
【问题描述】:

我在 youtube (venkat subramaniam) 上看到了 Let’s Get Lazy: Explore the Real Power of Streams 视频。 (大约 26-30 分钟)

在示例中,一个 for 循环:

List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10);
int result = 0;
for(int e: values){
  if(e > 3 && e % 2 == 0){
    result = e * 2;
    break;
  }
}  

有 8 个“单元操作”

按照他的例子:

public class MainClass {
    public static void main(String[] args) {

        List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10);

        System.out.println(
                numbers.stream()
                        .filter(e -> e > 3)
                        .filter(e -> e % 2 == 0)
                        .map(e -> e * 2)
                        .findFirst()
                        .orElse(0)
        );


    }
}

这段代码看起来有 21 个“单元操作”。

然后他推荐使用这个代码:

public class MainClass {
    public static void main(String[] args) {

        List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10);

        System.out.println(
                numbers.stream()
                        .filter(MainClass::isGT3)
                        .filter(MainClass::isEven)
                        .map(MainClass::doubleIt)
                        .findFirst()
                        .orElse(0)
        );


    }

    private static int doubleIt(Integer e) {
        return e * 2;
    }

    private static boolean isEven(Integer e) {
        return e % 2 == 0;
    }

    private static boolean isGT3(Integer e) {
        return e > 3;
    }
}

我真的很想明白,这怎么证明有 8 个单元操作而不是 21 个单元操作?

【问题讨论】:

  • 外部 YouTube 视频不是在此论坛上提出问题的一个很好的基础。理想情况下,您的问题应该以 text 的形式包含某人为您提供答案所需的所有信息。
  • @mah454 在找到匹配条件的第一个元素之前,视频中的计算总数与代码相关,为 (stream 10+filter 7+ filter 4)。但是使用适当的终端操作,您可以将计算评估为精确值,即使用 AtomicInteger 计数为每个操作增加 1。
  • @TimBiegeleisen 你是完全正确的。我编辑了提取部分的问题和需要正确询问的代码。感谢您的通知

标签: java java-8 stream java-stream


【解决方案1】:

不不不,你误解了这个想法。这个想法是对流的惰性评估。两者都需要“8 次计算”(用他的话),他试图说看起来需要 21 次。

这个

  numbers.stream()
                  .filter(MainClass::isGT3)
                  .filter(MainClass::isEven)
                  .map(MainClass::doubleIt)
                  .findFirst()
                  .orElse(0)

numbers.stream()
                .filter(e -> e > 3)
                .filter(e -> e % 2 == 0)
                .map(e -> e * 2)
                .findFirst()
                .orElse(0)

完全一样。唯一的区别是创建一个更具可读性的函数,仅此而已。 命令式代码:

List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10);
int result = 0;
for(int e: values){
  if(e > 3 && e % 2 == 0){
    result = e * 2;
    break;
  }
}

和流中的代码,计算完全相同,因为它们是按需调用的,这意味着它不会过滤所有&gt; 3,过滤所有% 2 == 0,不,它结合了这些操作,然后在调用终端函数时应用它(如findFirst()

如视频所示,如果在函数之间放置一些打印,它将显示 8 个操作:

public class Main {

    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10);

        System.out.println(processStream(numbers)); 
        System.out.println(getNumber(numbers));
        System.out.println(getNumberEfficient(numbers));
    }

    static Stream<Integer> processStream(List<Integer> numbers){
        return numbers.stream()
                .filter(e -> {
                    System.out.println("GT3: " + e);
                    return e > 3;
                })
                .filter(e -> {
                    System.out.println("is Even: " + e);
                    return e % 2 == 0;
                })
                .map(e -> {
                    System.out.println("times 2: " + e);
                    return e * 2;
                } );
    }

    static int getNumberEfficient(List<Integer> numbers){
        return numbers.stream()
                .filter(e -> {
                    System.out.println("GT3 and even: " + e);
                    return e > 3 && e % 2 == 0;
                })
                .map(e -> {
                    System.out.println("times 2: " + e);
                    return e * 2;
                } )
                .findFirst()
                .orElse(0);
    }

    static int getNumber(List<Integer> numbers){
        return numbers.stream()
                .filter(e -> {
                    System.out.println("GT3: " + e);
                    return e > 3;
                })
                .filter(e -> {
                    System.out.println("is Even: " + e);
                    return e % 2 == 0;
                })
                .map(e -> {
                    System.out.println("times 2: " + e);
                    return e * 2;
                } )
                .findFirst()
                .orElse(0);
    }
}

它会返回:

这是管道,没有执行任何操作,因为是懒惰

java.util.stream.ReferencePipeline$3@7ba4f24f

这些是 8 个操作:

GT3: 1
GT3: 2
GT3: 3
GT3: 5
is Even: 5
GT3: 4
is Even: 4
times 2: 4
8

而且这是一个小优化,不用做两个过滤器,可以只做一个,从8个减少到6个:

GT3 and even: 1
GT3 and even: 2
GT3 and even: 3
GT3 and even: 5
GT3 and even: 4
times 2: 4
8

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-27
    相关资源
    最近更新 更多