web-dev-qa-db-ja.com

非同期処理にJava 8 Streams APIを使用することは可能ですか?

私はJava 8でCompletionStage/CompletableFutureを使って非同期処理を行うために遊んでいます。これは非常にうまく機能します。ただし、ステージのイテレータ/ストリームの非同期処理を実行するステージが必要な場合がありますアイテム、これを行う方法がないようです。

具体的には、Stream.forEach()には、呼び出し後にすべてのアイテムが処理されるというセマンティクスがあります。私は同じことを望みますが、代わりにCompletionStageを使用します:

CompletionStage<Void> done = stream.forEach(...);
done.thenRun(...);

ストリームが非同期ストリーミングの結果によって裏付けられている場合、上記のコード自体でストリームが完了するのを待つよりもはるかに優れています。

これを現在のJava 8 APIでなんとかして構築することは可能ですか?回避策?

30
Rickard Öberg

私の知る限り、ストリームAPIは非同期イベント処理をサポートしていません。あなたはReactive Extensions for .NETのようなものを望んでいるように聞こえ、Netflixによって作成されたJavaと呼ばれるそのポート- RxJava があります。

RxJavaは、Java 8ストリーム(マップやフィルターなど))と同じ高レベル操作の多くをサポートし、非同期です。

更新:現在 反応ストリーム イニシアチブが機能しており、JDK 9には少なくともサポートが含まれるようですその一部は Flow クラスです。

22
Soulman

@KarolKrolが言及したように、CompletableFutureのストリームでそれを行うことができます。

cyclops-react と呼ばれるCompletableFutureのストリームでの作業を容易にするために、JDK8ストリームの上に構築するライブラリがあります。

ストリームを構成するには、cyclops-reactの流暢なpromise ike APIを使用するか、simple-reactの Stages を使用できます。

7
Adam Gent

cyclops-react (このライブラリの作成者です)は、ストリームを処理するための StreamUtils クラスを提供します。それが提供する関数の1つはfutureOperationsです。これは、標準のStreamターミナル操作(および一部)にひねりを加えたアクセスを提供します。Streamは非同期で実行され、結果はCompletableFuture内に返されます。 。例えば

 Stream<Integer> stream = Stream.of(1,2,3,4,5,6)
                                       .map(i->i+2);
 CompletableFuture<List<Integer>> asyncResult =  StreamUtils.futureOperations(stream,
                                             Executors.newFixedThreadPool(1))
                                       .collect(Collectors.toList());

Streamをラップし、同じ機能を提供する便利なクラスReactiveSeqも、Nice Fluent APIで提供されています

 CompletableFuture<List<Integer>> asyncResult = ReactiveSeq.of(1,2,3,4,5,6)
                                       .map(i->i+2)
                                       .futureOperations(
                                             Executors.newFixedThreadPool(1))
                                       .collect(Collectors.toList());

Adamが指摘したように cyclops-react FutureStreamsは、データを非同期に処理するように設計されています(FutureとStreamsを混合することにより)-I/Oのブロック(読み取りなど)を伴うマルチスレッド操作に特に適していますファイル、db呼び出し、残りの呼び出しなど)。

3
John McClean

ストリームを作成し、各要素をCompletionStageにマップし、CompletionStage.thenCombine()を使用して結果を収集することは可能ですが、結果のコードは、このような単純なものを使用するよりも読みやすくはありません。

    CompletionStage<Collection<Result>> collectionStage =
        CompletableFuture.completedFuture(
            new LinkedList<>()
        );

    for (Request request : requests) {
        CompletionStage<Result> resultStage = performRequest(request);
        collectionStage = collectionStage.thenCombine(
            resultStage,
            (collection, result) -> {
                collection.add(result);
                return collection;
            }
        );
    }

    return collectionStage;

この例は、読みやすさを失わない機能的なforEachに簡単に変換できます。ただし、ストリームのreduceまたはcollectを使用するには、それほど細かくないコードを追加する必要があります。

更新:CompletableFuture.allOfおよびCompletableFuture.join将来の結果のコレクションを将来の結果のコレクションに変換する、より読みやすい別の方法を提供します。