2016-10-15 3 views
3

ストリーム上でn番目のアイテムごとに非終端(および/または端末)操作を実行できる操作を探しています。たとえば、私は素数のストリームを使用しますが、ストリームは簡単にWebリクエスト、ユーザーアクション、またはその他のコールドデータやライブフィードを生成することもできます。このようなストリーム関数にJavaストリーム - 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 -> {}); 
+1

操作のこの種のは本当に最高のストリームなし*行われます*。。ストリームはこの種の操作には適していません。それを強制しようとしないでください。 –

答えて

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で示されるように、このコードはスレッドセーフではないため、並列ストリームでは使用できません。また、出力順序が乱れてしまいますので、とにかく並列ストリームで使用するのは実際には意味がありません。

+0

私はこれに行くつもりです。 @ saka1029はよりローカルで読みやすいアルゴリズムを持っていますが、あなたは一度だけmake-onceであり、どこでも使用でき、Streamhelperライブラリにうまく収まるようになります。ニース! –

+3

パラレルストリームを使用すると、 '15 20 10 5'のような出力に終わることがあります。また、この実装はスレッドセーフではないため、並列ストリームを使用するときに同期する必要があります。結果が得られる可能性があります。 – user140547

+0

@ user140547非常に良い点。ありがとうございました。 – Andreas

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 
+0

外部カウンタ変数なしでそれを行う方法があるかどうか疑問に思っていました。しかし、それは動作します!そのような機能を持っていると知っているストリームライブラリ –

+0

AtomicIntegerクラスや他のIntRefクラスを避けるためにcounter [0]をうまく使用してください:) –

5

count()は、全体の流れの上に行かなくても計算できるのであれば操作はすべてで呼び出されていない可能性がありとしてpeek()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()を使用すると、とにかく並列ストリームを使用することはそれほど有用ではないでしょう。

あなたは一般的な解決策が必要な場合、あなたはまたIntStreamほど効率的ではないかもしれないが、それでも多くのケースで十分なはずである「正常な」Streamを、使用することを試みることができる:

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); 
+0

聖なるカニ。それを知らなかった。ヘッドアップをありがとう。編集中... –

+1

@SaintHill:別のアプローチを追加しました。 – user140547

関連する問題