显示Java 8流处理的进度

时间:2018-06-10 15:57:42

标签: java java-8 java-stream

我有Stream处理数百万个元素。它背后的Map-Reduce算法需要几毫秒,因此任务完成大约需要20分钟。

Stream<MyData> myStream = readData();
MyResult result = myStream
    .map(row -> process(row))
    .peek(stat -> System.out.println("Hi, I processed another item"))
    .reduce(MyStat::aggregate);

我想要一种显示整体进度的方法,而不是每个元素打印一行(每秒产生数千行,需要时间,并且不提供有关整体进度的任何有用信息)。我想展示类似的东西:

 5% (08s)
10% (14s)
15% (20s)
...

最好(和/或最简单)的方法是什么?

3 个答案:

答案 0 :(得分:15)

首先,Streams并不是要实现这些任务(而不是传统的数据结构)。如果你已经知道你的流将处理多少元素,你可以使用以下选项,我重复一遍,而不是流的目标。

Stream<MyData> myStream = readData();
final AtomicInteger loader = new AtomicInteger();
int fivePercent = elementsCount / 20;
MyResult result = myStream
    .map(row -> process(row))
    .peek(stat -> {
        if (loader.incrementAndGet() % fivePercent == 0) {
            System.out.println(loader.get() + " elements on " + elementsCount + " treated");
            System.out.println((5*(loader.get() / fivePercent)) + "%");
        }
    })
    .reduce(MyStat::aggregate);

答案 1 :(得分:7)

正如其他人所指出的:这有一些警告。首先,流不应该用于这样的事情。

在更技术层面,人们可以进一步争论:

  • 流可以是无限的
  • 即使你知道元素的数量:这个数字可能因filterflatMap
  • 等操作而失真
  • 对于并行流,跟踪进度强制执行同步点
  • 如果有一个昂贵的终端操作(比如您的情况下的聚合),那么报告的进度可能甚至不能合理地反映计算时间

但是,记住这一点,可能对您的应用案例合理的一种方法是:

您可以创建一个传递给流Function<T,T>的{​​{1}}。 (至少,我更喜欢在流上使用map,如另一个答案中所建议的那样)。此函数可以使用peek来计算元素,从而跟踪进度。为了将单独的事物分开,这个进展可以转发到AtomicLong,这将照顾演示文稿

此处的“演示”指的是将此进度打印到控制台,标准化或百分比,指的是在创建消费者的任何地方都可以知道的大小。但是,消费者也可以仅处理打印,例如,每10个元素,或者如果自上一个元素以来已经过了至少5秒,则仅打印消息。

Consumer<Long>

答案 2 :(得分:1)

高度执行此操作的可能性取决于您free(ptr)source的类型。如果你有一个集合,并且你想对它应用一些操作,你可以这样做,因为你知道集合的大小,你可以保留已处理元素的数量。但在这种情况下也有一个警告。如果你将在流中进行并行计算,那么这也变得更加困难。

如果您从应用程序外部传输数据,则很难对流程进行建模,因为您不知道流何时结束。