Difference between Java 8 streams and RxJava observables
Short answer
All sequence/stream processing libs are offering very similar API for pipeline building. The differences are in API for handling multi-threading and composition of pipelines.
Long answer
RxJava is quite different from Stream. Of all JDK things, the closest to rx.Observable
is perhaps java.util.stream.Collector
Stream
+ CompletableFuture
combo (which comes at a cost of dealing with extra monad layer, i. e. having to handle conversion between Stream<CompletableFuture<T>>
and CompletableFuture<Stream<T>>
).
There are significant differences between Observable and Stream:
- Streams are pull-based, Observables are push-based. This may sound too abstract, but it has significant consequences that are very concrete.
- Stream can only be used once, Observable can be subscribed to many times.
Stream#parallel()
splits sequence into partitions,Observable#subscribeOn()
andObservable#observeOn()
do not; it is tricky to emulateStream#parallel()
behavior with Observable, it once had.parallel()
method but this method caused so much confusion that.parallel()
support was moved to separate repository: ReactiveX/RxJavaParallel: Experimental Parallel Extensions for RxJava. More details are in another answer.Stream#parallel()
does not allow to specify a thread pool to use, unlike most of RxJava methods accepting optional Scheduler. Since all stream instances in a JVM use the same fork-join pool, adding.parallel()
can accidentally affect the behaviour in another module of your program.- Streams are lacking time-related operations like
Observable#interval()
,Observable#window()
and many others; this is mostly because Streams are pull-based, and upstream has no control on when to emit next element downstream. - Streams offer restricted set of operations in comparison with RxJava. E.g. Streams are lacking cut-off operations (
takeWhile()
,takeUntil()
); workaround usingStream#anyMatch()
is limited: it is terminal operation, so you can't use it more than once per stream - As of JDK 8, there's no
Stream#zip()
operation, which is quite useful sometimes. Streams are hard to construct by yourself, Observable can be constructed by many waysEDIT: As noted in comments, there are ways to construct Stream. However, since there's no non-terminal short-circuiting, you can't e. g. easily generate Stream of lines in file (JDK providesFiles#lines()
andBufferedReader#lines()
out of the box though, and other similar scenarios can be managed by constructing Stream from Iterator).- Observable offers resource management facility (
Observable#using()
); you can wrap IO stream or mutex with it and be sure that the user will not forget to free the resource - it will be disposed automatically on subscription termination; Stream hasonClose(Runnable)
method, but you have to call it manually or via try-with-resources. E. g. you have to keep in mind thatFiles#lines()
must be enclosed in try-with-resources block. - Observables are synchronized all the way through (I didn't actually check whether the same is true for Streams). This spares you from thinking whether basic operations are thread-safe (the answer is always 'yes', unless there's a bug), but the concurrency-related overhead will be there, no matter if your code need it or not.
Round-up
RxJava differs from Streams significantly. Real RxJava alternatives are other implementations of ReactiveStreams, e. g. relevant part of Akka.
Update
There's trick to use non-default fork-join pool for Stream#parallel
, see Custom thread pool in Java 8 parallel stream.
Update
All of the above is based on the experience with RxJava 1.x. Now that RxJava 2.x is here, this answer may be out-of-date.
There are a few technical and conceptional differences, for example, Java 8 streams are single use, pull based, synchronous sequences of values whereas RxJava Observables are re-observable, adaptively push-pull based, potentially asynchronous sequences of values. RxJava is aimed at Java 6+ and works on Android as well.
Java 8 Stream and RxJava looks pretty similar. They have look alike operators (filter, map, flatMap...) but are not built for the same usage.
You can perform asynchonus tasks using RxJava.
With Java 8 stream, you'll traverse items of your collection.
You can do pretty much the same thing in RxJava (traverse items of a collection) but, as RxJava is focussed on concurrent task, ..., it use synchronization, latch, ... So the same task using RxJava may be slower than with Java 8 stream.
RxJava can be compared to CompletableFuture
, but that can be able to compute more than just one value.