java Thread.sleep对流处理没有影响吗?[duplicate]

r3i60tvu  于 2023-01-01  发布在  Java
关注(0)|答案(3)|浏览(133)
    • 此问题在此处已有答案**:

Intermediate stream operations not evaluated on count(3个答案)
2小时前关门了。
以下程序摘自Jeanne Boyarsky和Scott Selikoff的OCP学习指南:

import java.util.*;

class WhaleDataCalculator {
    public int processRecord(int input) {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            // Handle interrupted exception
        }
        return input + 1;
    }

    public void processAllData(List<Integer> data) {
        data.stream().map(a -> processRecord(a)).count();
    }

    public static void main(String[] args) {
        WhaleDataCalculator calculator = new WhaleDataCalculator();
        // Define the data
        List<Integer> data = new ArrayList<Integer>();
        for (int i = 0; i < 4000; i++)
            data.add(i);
        // Process the data
        long start = System.currentTimeMillis();
        calculator.processAllData(data);
        double time = (System.currentTimeMillis() - start) / 1000.0;
        // Report results
        System.out.println("\nTasks completed in: " + time + " seconds");
    }
}

提交人声称
假设有4,000条记录,每条记录需要10毫秒的时间来处理,通过使用串行流(),结果将需要大约40秒的时间来完成此任务。
但是,当我在系统中运行此程序时,每次运行所需的时间在0.006秒到0.009秒之间。
差异在哪里?

ajsxfq5m

ajsxfq5m1#

这是因为使用了count,它在以后的Java版本中执行了一个技巧。
因为你只对元素的数量感兴趣,count将尝试直接从源代码中获取大小,并跳过大多数其他操作,这是可能的,因为你只执行map而不是filter,所以元素的数量不会改变。
如果添加peek(System.out::println),也不会看到输出。
如果调用forEach而不是count,则运行代码可能需要40秒。

o2rvlv0m

o2rvlv0m2#

由于Java 9操作count()已经过优化,因此如果在流的初始化过程中(当流水线的级被链接时)结果是不存在可以改变流源中的元素数目的操作允许评估其包含的元素数目,则count()不触发流水线的执行,而是询问源"你有多少这样的人?"并立即返回值。
因此,在运行processAllData()时,将构造一个Stream示例,然后该方法将终止,因为实际上没有处理任何元素。
以下是文档中的一段引文:

    • API注解:**

如果实现能够直接从流源计算计数,则可以选择不执行流管道(顺序或并行)。在这种情况下,将不会遍历源元素,也不会评估中间操作。具有副作用的行为参数可能会受到影响,除非是无害的情况(如调试),否则强烈建议不要执行这些参数。例如,考虑以下流:

List<String> l = Arrays.asList("A", "B", "C", "D");
 long count = l.stream().peek(System.out::println).count();

由流源List覆盖的元素的数目是已知的,并且中间操作peek不向流中注入元素或从流中移除元素(如flatMapfilter操作的情况)。因此,countList的大小,并且不需要执行流水线,并且作为副作用,打印出列表元素。
顺便说一句,除了这个测试背后的技巧之外,这个例子不需要使用Stream API,因为count()返回的值被忽略了,所需要的只是在列表的每个元素上触发一个副作用,所以可以使用Iterable.forEach()

public void processAllData(List<Integer> data) {
    data.forEach(a -> processRecord(a));
}
tpgth1q7

tpgth1q73#

.map(a -> processRecord(a))的调用根本没有运行,原因是您正在使用1.8以上的JDK版本运行此程序。
让我们举这个例子,让它更容易理解:

long number = Stream.of("x", "x", "x").map(e -> {
            System.out.println("Hello");
            return e;
        }).count();
        
        System.out.println(number);

尝试使用JDK 1.8运行它,然后使用JDK 11运行它。
在Java 8中,count()充当终端操作,所有中间操作(这里是map方法)都将被执行,map操作将被执行并将打印hello消息。您将得到以下输出:

Hello
Hello
Hello
3

在大于1.8的Java版本中,这里以11为例,如果不存在能够改变流的元素数目的中间操作,则Java能够直接确定流的元素数目(例如:filter()),没有中间方法会被执行,只有count方法会被执行,所以你不会看到任何hello消息,但是这个流的元素的数量会被计算出来,你可以使用它。你的输出将是这样的:

3

如果您希望在高于1.8的Java版本中看到hello消息,您应该在流管道中添加一个中间操作,该操作可以更改流的元素数量,让我们在管道中添加filter方法,并查看java 11上的输出:

long number = Stream.of("x", "x", "x").map(e -> {
            System.out.println("Hello");
            return e;
        }).filter(element-> element.equals("x")).count();
        
        System.out.println(number);

输出如下:

Hello
Hello
Hello
3

相关问题