Java 8ストリームとRxJavaオブザーバブルの違い


144

Java 8ストリームはRxJavaオブザーバブルに似ていますか?

Java 8ストリーム定義:

新しいjava.util.streamパッケージのクラスは、要素のストリームに対する関数形式の操作をサポートするためのストリームAPIを提供します。


8
参考までに、JDK 9でRxJavaのようなクラスをさらに導入する提案があります。jsr166
John Vint

@JohnVintこの提案のステータスは何ですか。実際に飛行しますか?
IgorGanapolsky 2016年

2
@IgorGanapolskyそうそう、それは間違いなくjdk9に入るように見えます。cr.openjdk.java.net/~martin/webrevs/openjdk9/…。RxJavaをフローgithub.com/akarnokd/RxJavaUtilConcurrentFlowに移植することさえできます。
John Vint 2016年

私は、これは本当に古い質問ですけど、私は最近、被写体に洞察力のテイクを持っており、Java9に更新されヴェンカトSubramaniamすることで、この偉大な講演に出席:youtube.com/watch?v=kfSSKM9y_0Eを。RxJavaを深く理解している人にとっては興味深いかもしれません。
ペドロ

回答:


152

TL; DR:すべてのシーケンス/ストリーム処理ライブラリは、パイプライン構築用の非常に類似したAPIを提供しています。違いは、マルチスレッド処理とパイプラインの構成を処理するためのAPIです。

RxJavaはStreamとはかなり異なります。すべてのJDKの中で、rx.Observableに最も近いのは、おそらくjava.util.stream.Collector Stream + CompletableFutureコンボです(これは、追加のモナドレイヤーを処理するコスト、つまりとの間の変換を処理する必要がStream<CompletableFuture<T>>ありますCompletableFuture<Stream<T>>)。

ObservableとStreamの間には大きな違いがあります。

  • ストリームはプルベースであり、オブザーバブルはプッシュベースです。これは抽象的に聞こえるかもしれませんが、非常に具体的な重大な結果をもたらします。
  • ストリームは1回だけ使用でき、Observableは何度でも購読できます
  • Stream#parallel()パーティションにシーケンスを分割し、Observable#subscribeOn()そしてObservable#observeOn()ません。Stream#parallel()Observableで動作をエミュレートするのはトリッキーです。以前は.parallel()メソッドがありましたが、このメソッドは混乱を引き起こし、.parallel()サポートがgithubのRxJavaParallelの別のリポジトリに移動しました。詳細は別の回答にあります。
  • Stream#parallel()オプションのスケジューラを受け入れるほとんどのRxJavaメソッドとは異なり、では使用するスレッドプールを指定できません。以来、すべての JVMにおけるストリーム・インスタンスは追加して、同じプールをフォークは、結合を使用.parallel()誤ってプログラムの別のモジュールで動作に影響を与えることができます
  • ストリームは次のように時間に関連する業務を欠いているObservable#interval()Observable#window()と多くの他。これは主に、ストリームがプルベースであり、上流が次の要素を下流に放出するタイミングを制御できないためです
  • ストリームは、RxJavaと比較して制限された一連の操作を提供します。たとえば、ストリームにはカットオフ操作がありません(takeWhile()takeUntil())。使用する回避策Stream#anyMatch()は限られています。これは端末操作であるため、1つのストリームで複数回使用することはできません
  • JDK 8以降、Stream#zipオペレーションはなくなりました。
  • ストリームは、自分で構築するのは難しいです、観察可能は多くの方法をすることによって構築することができる EDIT:としては、コメントで指摘し、ストリームを構築する方法があります。ただし、非端子短絡がないため、たとえばファイルにStream of linesを簡単に生成することはできません(JDKはそのままでFiles#linesおよびBufferedReader#linesを提供し、他の同様のシナリオはStreamを構築することで管理できますイテレーターから)。
  • Observableはリソース管理機能を提供しています(Observable#using()); IOストリームまたはミューテックスをラップして、ユーザーがリソースを解放するのを忘れないようにしてください-サブスクリプションの終了時に自動的に破棄されます。ストリームにはonClose(Runnable)メソッドがありますが、手動でまたはtry-with-resourcesを介して呼び出す必要があります。例:Files#lines()はtry-with-resourcesブロックで囲む必要があることに注意してください。
  • Observableはずっと同期されています(Streamsについても同様です)。これにより、基本的な操作がスレッドセーフであるかどうかを考える手間が省けます(バグがない限り、答えは常に「はい」です)が、コードに必要かどうかに関係なく、同時実行性に関連するオーバーヘッドが発生します。

まとめ:RxJavaはStreamsと大きく異なります。実際のRxJavaの代替は、ReactiveStreamsの他の実装です(例:Akkaの関連部分)。

アップデート。にデフォルト以外のfork-joinプールを使用するトリックがあります。Java8 並列ストリームのカスタムスレッドプールをStream#parallel参照してください

アップデート。上記のすべては、RxJava 1.xでの経験に基づいています。今すぐことRxJavaの2.xはここにある、この答えは時代遅れのかもしれません。


2
ストリームを構築するのが難しいのはなぜですか?この記事によると、それは簡単なようだ:oracle.com/technetwork/articles/java/...
IgorGanapolsky

2
コレクション、入力ストリーム、ディレクトリファイルなど、「ストリーム」メソッドを持つクラスはかなりたくさんありますが、カスタムループからストリームを作成したい場合はどうでしょう。たとえば、データベースカーソルを反復処理する場合はどうでしょうか。これまでに見つけた最良の方法は、イテレーターを作成し、それをスプリッターでラップし、最後にStreamSupport#fromSpliteratorを呼び出すことです。シンプルなケースIMHOには接着剤が多すぎます。Stream.iterateもありますが、無限のストリームを生成します。その場合、ストリームをカットする唯一の方法はStream#anyMatchですが、これはターミナルオペレーションであるため、ストリームプロデューサーとコンシューマーを分離することはできません
Kirill Gamazkov

2
RxJavaには、Observable.fromCallable、Observable.createなどがあります。または、無制限のObservableを安全に生成し、「。takeWhile(condition)」と
発声

1
ストリームを自分で構築するのは難しくありません。Stream.generate()独自のSupplier<U>実装を呼び出して渡すだけで、ストリーム内の次の項目を提供する1つの単純なメソッドになります。他にもたくさんの方法があります。Stream以前の値に依存するシーケンスを簡単に構築するために、interate()メソッドを使用できます。すべてCollectionstream()メソッドがあり、可変引数または配列からをStream.of()構築しStreamます。最後に、 StreamSupportスプリッターを使用したより高度なストリーム作成またはストリームプリミティブタイプをサポートしています。
jbx 2016年

「ストリームにはカットオフ操作がありません(takeWhile()takeUntil())」。-JDK9には、これらがtakeWhile()およびdropWhile()に含まれている
Abdul

50

Java 8 StreamとRxJavaはかなり似ています。それらは似た演算子(filter、map、flatMap ...)を持っていますが、同じ使用法のために構築されていません。

RxJavaを使用して非同期タスクを実行できます。

Java 8ストリームでは、コレクションのアイテムをトラバースします。

RxJava(コレクションのトラバースアイテム)でもほぼ同じことができますが、RxJavaは並行タスクに重点を置いているため、同期、ラッチなどを使用します。したがって、RxJavaを使用する同じタスクは、 Java 8ストリーム。

RxJavaはと比較CompletableFutureできますが、1つ以上の値を計算できます。


12
ストリームトラバーサルについてのステートメントは、非並列ストリームにのみ当てはまることに注意してください。 parallelStream/シンプルトラバーサルの同様の同期をサポートしてマップする/フィルタリングなど。
ジョンVintの

2
「したがって、RxJavaを使用した同じタスクは、Java 8ストリームを使用した場合よりも遅くなる可能性があります。」普遍的に当てはまり、手元のタスクに大きく依存します。
daschl 2016

1
RxJavaを使用した同じタスクはJava 8ストリームを使用した場合よりも遅くなる可能性があると言ってよかった これは、多くのRxJavaユーザーが認識していない非常に重要な違いです。
IgorGanapolsky 2016年

RxJavaはデフォルトで同期です。遅いかもしれないというあなたの声明を裏付けるベンチマークはありますか?
MarcinKoziński、2016年

6
@あなたがこのベンチマークを確認することができますマルチン・koziński:twitter.com/akarnokd/status/752465265091309568
dwursteisen

37

いくつかの技術的および概念的な違いがあります。たとえば、Java 8ストリームは値の単一使用のプルベースの同期シーケンスですが、RxJava Observableは再観測可能で、適応的にプッシュプルベースの非同期の値のシーケンスである可能性があります。RxJavaはJava 6以降を対象としており、Androidでも動作します。


4
RxJavaを含む典型的なコードは、Java 8以降でのみ利用可能なラムダを多用します。したがって、RxをJava 6で使用できますが、コードは騒々しいでしょう
Kirill Gamazkov

1
同様の違いは、Rx Observableがサブスクライブ解除されるまで無期限に存続できることです。Java 8ストリームは、デフォルトでは操作で終了します。
IgorGanapolsky 2016年

2
@KirillGamazkovを使用できretrolambdaを Javaの6.ターゲットとする場合、あなたのコードをきれいに作るために
マルチンKoziński

コトリンはレトロフィットよりもセクシーに見える
キリル・ガマスコフ2017年

30

Java 8ストリームはプルベースです。各アイテムを消費するJava 8ストリームを反復処理します。そして、それは無限のストリームになる可能性があります。

RXJava Observableはデフォルトでプッシュベースです。Observableをサブスクライブすると、次のアイテムが到着したonNextとき()、ストリームが完了したonCompletedとき()、またはエラーが発生したとき()に通知されますonError。であるのでObservableあなたが受け取るonNextonCompletedonErrorイベントは、あなたが別の組み合わせのようないくつかの強力な機能を行うことができますObservable新しいものにSを(zipmergeconcat)。他にできることは、キャッシング、スロットルなどです...そして、それは多かれ少なかれ同じ言語を異なる言語で使用しています(RxJava、C#のRX、RxJSなど)。

デフォルトでは、RxJavaはシングルスレッドです。スケジューラの使用を開始しない限り、すべてが同じスレッドで発生します。


StreamにはforEachがあります。これはonNextとほとんど同じです
ポール

実際、ストリームは通常はターミナルです。「ストリームパイプラインを閉じる操作は端末操作と呼ばれます。これらは、リスト、整数、またはvoid(非ストリーム型)などのパイプラインから結果を生成します。」〜oracle.com
technetwork

26

既存の答えは包括的で正しいですが、初心者のための明確な例は欠けています。「プッシュ/プルベース」や「再観測可能」などの用語を具体的に説明します。 :私はこの用語Observable(これは天国のためのストリームです)が嫌いなので、単にJ8 vs RXストリームを指します。

整数のリストを考えてみましょう。

digits = [1,2,3,4,5]

J8 Streamは、コレクションを変更するためのユーティリティです。たとえば、数字でも抽出できます。

evens = digits.stream().filter(x -> x%2).collect(Collectors.toList())

これは基本的にPythonのmap、filter、reduceであり、Javaに非常に便利な(そして長い間遅れている)追加です。しかし、事前に数字が収集されていなかった場合、つまり、アプリの実行中に数字がストリーミングされていた場合は、イベントをリアルタイムでフィルタリングできますか。

別のスレッドプロセスが、アプリの実行中にランダムな時間に整数を出力していると想像してください(---時間を表します)

digits = 12345---6------7--8--9-10--------11--12

RXでは、evenすることができます反応する各新しい桁に、リアルタイムでフィルタを適用

even = -2-4-----6---------8----10------------12

入力リストと出力リストを保存する必要はありません。出力リストが必要な場合は、ストリーミングも可能です。実際、すべてがストリームです。

evens_stored = even.collect()  

これが、「ステートレス」や「機能的」などの用語がRXに関連している理由です。


しかし、5は偶数ではありません...そして、それはR8ストリームが非同期である一方で、J8ストリームは同期であるように見えますか?
フランクリンユー

1
@FranklinYu 5つのタイプミスを修正しました。それは正しいかもしれませんが、同期対非同期の観点からはあまり考えず、命令対機能の観点からはもっと考えます。J8では、最初にすべてのアイテムを収集し、次にフィルターを適用します。RXでは、データとは無関係にフィルター関数を定義し、それを偶数のソース(ライブストリーム、またはJavaコレクション)に関連付けます...それはまったく異なるプログラミングモデルです
Adam Hughes

私はこれにとても驚いています。Javaストリームはデータストリーミングで作成できると確信しています。反対の理由は何ですか。
Vic Seedoubleyew

4

RxJavaは、リアクティブストリームイニシアチブとも密接に関連しており、それ自体をリアクティブストリームAPIの単純な実装と見なしています(たとえば、Akkaストリームの実装と比較して)。主な違いは、リアクティブストリームはバックプレッシャを処理できるように設計されていることですが、リアクティブストリームページを見ると、アイデアがわかります。彼らは彼らの目標をかなりよく説明しており、ストリームは反応的なマニフェストにも密接に関連しています。

Java 8ストリームは、ほぼ無制限のコレクションの実装であり、Scala StreamまたはClojure lazy seqに非常に似ています。


3

Java 8 Streamsは、マルチコアアーキテクチャを活用しながら、非常に大きなコレクションを効率的に処理できるようにします。対照的に、RxJavaはデフォルトで(スケジューラなしで)シングルスレッドです。そのため、自分でロジックをコーディングしない限り、RxJavaはマルチコアマシンを利用しません。


4
.parallel()を呼び出さない限り、ストリームもデフォルトでシングルスレッドです。また、Rxは同時実行性をより詳細に制御します。
Kirill Gamazkov 2017年

@KirillGamazkov Kotlinコルーチンフロー(Java8ストリームに基づく)が構造化された同時実行性をサポートするようになりました:kotlinlang.org/docs/reference/coroutines/flow.html#flows
IgorGanapolsky

本当ですが、フローと構造化された並行性については何も言っていません。私の2つのポイントは次のとおりです。1)明示的に変更しない限り、StreamとRxの両方がシングルスレッドです。2)Rxでは、どのスレッドプールでどのステップを実行するかをきめ細かく制御できます。これに対して、Streamsでは「なんとかして並列化する」としか言えません
Kirill Gamazkov

「スレッドプールが何のために必要なのか」という質問の要点は本当にわかりません。あなたが言ったように、「非常に大きなコレクションの処理を効率的に可能にするため」。または、タスクのIOにバインドされた部分を別のスレッドプールで実行したい場合があります。私はあなたの質問の背後にある意図を理解していなかったと思います。再試行?
Kirill Gamazkov

1
Schedulersクラスの静的メソッドを使用すると、事前定義されたスレッドプールを取得したり、Executorから作成したりできます。参照してくださいreactivex.io/RxJava/2.x/javadoc/io/reactivex/schedulers/...
キリルGamazkov
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.