我有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)
...
最好(和/或最简单)的方法是什么?
答案 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)
正如其他人所指出的:这有一些警告。首先,流不应该用于这样的事情。
在更技术层面,人们可以进一步争论:
filter
或flatMap
但是,记住这一点,可能对您的应用案例合理的一种方法是:
您可以创建一个传递给流Function<T,T>
的{{1}}。 (至少,我更喜欢在流上使用map
,如另一个答案中所建议的那样)。此函数可以使用peek
来计算元素,从而跟踪进度。为了将单独的事物分开,这个进展可以转发到AtomicLong
,这将照顾演示文稿
此处的“演示”指的是将此进度打印到控制台,标准化或百分比,指的是在创建消费者的任何地方都可以知道的大小。但是,消费者也可以仅处理打印,例如,每10个元素,或者如果自上一个元素以来已经过了至少5秒,则仅打印消息。
Consumer<Long>
答案 2 :(得分:1)
高度执行此操作的可能性取决于您free(ptr)
中source
的类型。如果你有一个集合,并且你想对它应用一些操作,你可以这样做,因为你知道集合的大小,你可以保留已处理元素的数量。但在这种情况下也有一个警告。如果你将在流中进行并行计算,那么这也变得更加困难。
如果您从应用程序外部传输数据,则很难对流程进行建模,因为您不知道流何时结束。