我正在寻找一个Stream操作,使我能够每隔n项执行一次非终端(和/或终端)操作。虽然我使用素数流,例如,流可以很容易地生成网络请求,用户操作或其他一些冷数据或实时源。
由此:
Duration start = Duration.ofNanos(System.nanoTime());
IntStream.iterate(2, n -> n + 1)
.filter(Findprimes::isPrime)
.limit(1_000_1000 * 10)
.forEach(System.out::println);
System.out.println("Duration: " + Duration.ofNanos(System.nanoTime()).minus(start));
对于这样的流函数:
IntStream.iterate(2, n -> n + 1)
.filter(Findprimes::isPrime)
.limit(1_000_1000 * 10)
.peekEvery(10, System.out::println)
.forEach( it -> {});
答案 0 :(得分:6)
创建一个帮助方法来包装peek()
消费者:
public static IntConsumer every(int count, IntConsumer consumer) {
if (count <= 0)
throw new IllegalArgumentException("Count must be >1: Got " + count);
return new IntConsumer() {
private int i;
@Override
public void accept(int value) {
if (++this.i == count) {
consumer.accept(value);
this.i = 0;
}
}
};
}
您现在几乎可以按照自己的意愿使用它:
IntStream.rangeClosed(1, 20)
.peek(every(5, System.out::println))
.count();
输出
5
10
15
20
辅助方法可以放在实用程序类中并静态导入,类似于Collectors
类只是静态辅助方法。
正如comment中@ user140547所述,此代码不是线程安全的,因此不能与并行流一起使用。此外,输出顺序会搞砸,所以无论如何将它与并行流一起使用真的没有意义。
答案 1 :(得分:5)
依靠peek()
和count()
作为,如果count()
可以,则可能根本不会调用该操作,这不是一个好主意无需遍历整个流程即可计算出来。即使它现在有效,但这并不意味着它将来也会起作用。请参阅Java 9中的javadoc of Stream.count()
。
更好地使用forEach()
。
对于问题本身:在像简单迭代这样的特殊情况下,你可以过滤你的对象,如。
Stream.iterate(2, n->n+1)
.limit(20)
.filter(n->(n-2)%5==0 && n!=2)
.forEach(System.out::println);
这当然不适用于其他可能使用有状态IntConsumer
的情况。如果使用iterate()
,那么无论如何使用并行流可能没那么有用。
如果你想要一个通用的解决方案,你也可以尝试使用&#34; normal&#34; Stream
,可能效率不如IntStream
,但在很多情况下仍然可以满足要求:
class Tuple{ // ctor, getter/setter omitted
int index;
int value;
}
然后你可以这样做:
Stream.iterate( new Tuple(1,2),t-> new Tuple(t.index+1,t.value*2))
.limit(30)
.filter(t->t.index %5 == 0)
.forEach(System.out::println);
如果您必须使用peek()
,您也可以
.peek(t->{if (t.index %5 == 0) System.out.println(t);})
或者如果你添加方法
static Tuple initialTuple(int value){
return new Tuple(1,value);
}
static UnaryOperator<Tuple> createNextTuple(IntUnaryOperator f){
return current -> new Tuple(current.index+1,f.applyAsInt(current.value));
}
static Consumer<Tuple> every(int n,IntConsumer consumer){
return tuple -> {if (tuple.index % n == 0) consumer.accept(tuple.value);};
}
您也可以(使用静态导入):
Stream.iterate( initialTuple(2), createNextTuple(x->x*2))
.limit(30)
.peek(every(5,System.out::println))
.forEach(System.out::println);
答案 2 :(得分:1)
试试这个。
int[] counter = {0};
long result = IntStream.iterate(2, n -> n + 1)
.filter(Findprimes::isPrime)
.limit(100)
.peek(x -> { if (counter[0]++ % 10 == 0) System.out.print(x + " ");} )
.count();
结果:
2 31 73 127 179 233 283 353 419 467