[英]Show progress of Java 8 stream processing
我有一個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)
...
最好(和/或最簡單)的方法是什么?
首先,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);
正如其他人所指出的:這有一些警告。 首先,流不應該用於這樣的事情。
在更技術層面,人們可以進一步爭論:
filter
或flatMap
等操作扭曲 但是,記住這一點,對您的應用案例可能合理的一種方法是:
您可以創建一個傳遞給流map
的Function<T,T>
。 (至少,我更喜歡在流上使用peek
,如另一個答案所示)。 此功能可以使用AtomicLong
計算元素來跟蹤進度。 為了將單獨的事物分開,可以將此進度轉發給Consumer<Long>
,它將負責演示
這里的“演示”是指將此進度打印到控制台,標准化或百分比,指的是在創建消費者的任何地方都可以知道的大小。 但是,消費者也可以僅處理打印,例如,每10個元素,或者如果自上一個元素以來已經過了至少5秒,則僅打印消息。
import java.util.Iterator;
import java.util.Locale;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.function.LongConsumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class StreamProgress
{
public static void main(String[] args)
{
int size = 250;
Stream<Integer> stream = readData(size);
LongConsumer progressConsumer = progress ->
{
// "Filter" the output here: Report only every 10th element
if (progress % 10 == 0)
{
double relative = (double) progress / (size - 1);
double percent = relative * 100;
System.out.printf(Locale.ENGLISH,
"Progress %8d, relative %2.5f, percent %3.2f\n",
progress, relative, percent);
}
};
Integer result = stream
.map(element -> process(element))
.map(progressMapper(progressConsumer))
.reduce(0, (a, b) -> a + b);
System.out.println("result " + result);
}
private static <T> Function<T, T> progressMapper(
LongConsumer progressConsumer)
{
AtomicLong counter = new AtomicLong(0);
return t ->
{
long n = counter.getAndIncrement();
progressConsumer.accept(n);
return t;
};
}
private static Integer process(Integer element)
{
return element * 2;
}
private static Stream<Integer> readData(int size)
{
Iterator<Integer> iterator = new Iterator<Integer>()
{
int n = 0;
@Override
public Integer next()
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return n++;
}
@Override
public boolean hasNext()
{
return n < size;
}
};
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(
iterator, Spliterator.ORDERED), false);
}
}
這樣做的可能性很大程度上取決於您在stream
中使用的source
類型。 如果你有一個集合,並且你想對它應用一些操作,你可以這樣做,因為你知道集合的大小,你可以保留已處理元素的數量。 但在這種情況下也有一個警告。 如果您將在流中進行並行計算,那么這也變得更加困難。
在您從應用程序外部傳輸數據的情況下,您很難對流程進行建模,因為您不知道流何時結束。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.