正如其他人所指出的:这有一些警告。首先,流不应该用于这样的事情。
在更技术性的层面上,人们可以进一步争论:
- 流可以是无限的
- 即使您知道元素的数量:此数字也可能被以下操作扭曲,例如
filter
flatMap
- 对于并行流,跟踪进度将强制实施同步点
- 如果存在昂贵的终端操作(如您情况下的聚合),则报告的进度甚至可能不能合理地反映计算时间
但是,请记住这一点,对于您的应用程序案例来说,一种可能合理的方法是:
您可以创建一个传递给流 a 的。(至少,我更喜欢在流上使用,正如另一个答案所建议的那样)。此函数可以跟踪进度,使用 用于计数元素的 。为了将单独的事物分开,可以将此进度转发给 ,这将负责演示Function<T,T>
map
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);
}
}