web-dev-qa-db-fra.com

Différence entre Java 8 flux et les observables RxJava

Les flux Java 8 sont-ils similaires aux observables RxJava?

Définition du flux Java 8:

Les classes du nouveau package Java.util.stream fournissent une API de flux permettant de prendre en charge les opérations de style fonctionnel sur les flux d'éléments.

131
3xplore

TL; DR : toutes les bibliothèques de traitement de séquence/flux offrent une API très similaire pour la construction de pipeline. Les différences concernent l’API pour la gestion du multi-threading et la composition des pipelines.

RxJava est assez différent de Stream. De toutes les choses JDK, le plus proche de rx.Observable est peut-être Java.util.stream.Collector Combinaison Stream + CompletableFuture (qui a un coût de traitement avec une couche monad supplémentaire, c’est-à-dire qu’elle doit gérer la conversion entre Stream<CompletableFuture<T>> et CompletableFuture<Stream<T>>).

Il existe des différences significatives entre Observable et Stream:

  • Les flux sont basés sur la traction, les observables sur la base Push. Cela peut paraître trop abstrait, mais cela a des conséquences importantes et très concrètes.
  • Stream ne peut être utilisé qu'une seule fois, Observable peut être abonné plusieurs fois
  • Stream#parallel() divise la séquence en partitions, Observable#subscribeOn() et Observable#observeOn() do not; il est délicat d'émuler Stream#parallel() comportement avec Observable, il utilisait jadis la méthode .parallel(), mais cette méthode était tellement source de confusion que le support de .parallel() a été déplacé vers un référentiel distinct sur github, RxJavaParallel. Plus de détails sont dans ne autre réponse .
  • Stream#parallel() ne permet pas de spécifier un pool de threads à utiliser, contrairement à la plupart des méthodes RxJava acceptant le planificateur facultatif. Etant donné que , toutes les instances de flux d'une machine virtuelle utilisent le même pool de jointures à la fourche, l'ajout de .parallel() peut affecter accidentellement le comportement d'un autre module de votre programme.
  • Les flux manquent d'opérations liées au temps telles que Observable#interval(), Observable#window() et beaucoup d'autres; c'est principalement parce que les flux sont basés sur la traction
  • Les flux offrent un ensemble d'opérations restreint par rapport à RxJava. Par exemple. Les flux manquent d'opérations de coupure (takeWhile(), takeUntil()); La solution de contournement à l'aide de Stream#anyMatch() est limitée: il s'agit d'un terminal, vous ne pouvez donc pas l'utiliser plus d'une fois par flux.
  • A partir de JDK 8, il n’ya pas d’opération Stream # Zip, ce qui est très utile parfois
  • Les cours d'eau sont difficiles à construire par vous-même, Observable peut être construit de plusieurs façons  EDIT: Comme indiqué dans les commentaires, il existe des moyens de construire Stream. Cependant, puisqu'il n'y a pas de court-circuitage non terminal, vous ne pouvez pas e. g. générer facilement un flux de lignes dans un fichier (le JDK fournit des lignes Files # et BufferedReader # prêtes à l'emploi, et d'autres scénarios similaires peuvent être gérés en construisant Stream à partir d'Iterator).
  • Fonction de gestion des ressources des offres observables (Observable#using()); vous pouvez envelopper IO stream ou mutex avec celui-ci et vous assurer que l'utilisateur n'oubliera pas de libérer la ressource - elle sera automatiquement supprimée à la fin de l'abonnement; Le flux a la méthode onClose(Runnable), mais vous devez l’appeler manuellement ou via des ressources d’essai. Par exemple. vous devez garder à l'esprit que Files # lines () doit être enfermé dans le bloc try-with-resources.
  • Les observables sont entièrement synchronisés (je n'ai pas vérifié si la même chose était vraie pour les Streams). Cela vous évite de savoir si les opérations de base sont sécurisées pour les threads (la réponse est toujours "oui", sauf en cas de bogue), mais la surcharge liée à la simultanéité sera présente, que votre code en ait besoin ou non.

Round-up: RxJava diffère des Streams de manière significative. Les alternatives réelles à RxJava sont d'autres implémentations de ReactiveStreams , e. g. partie pertinente de Akka.

Mettre à jour . Il existe un truc pour utiliser un pool de jointure fork par joint non défini par défaut pour Stream#parallel, voir Pool de threads personnalisé dans le flux parallèle Java 8

Mettre à jour . Tout ce qui précède est basé sur l'expérience de RxJava 1.x. Maintenant que RxJava 2.x est ici , cette réponse peut être obsolète.

138
Kirill Gamazkov

Java 8 Stream et RxJava sont assez similaires. Ils ont des opérateurs similaires (filtre, carte, flatMap ...) mais ne sont pas construits pour le même usage.

Vous pouvez effectuer des tâches asynchrone à l'aide de RxJava.

Avec le flux Java 8, vous allez parcourir les éléments de votre collection.

Vous pouvez faire à peu près la même chose dans RxJava (parcourir les éléments d’une collection) mais, comme RxJava est concentré sur une tâche simultanée, ..., il utilise la synchronisation, le verrouillage, ... Ainsi, la même tâche utilisant RxJava peut être plus lente que avec Java 8 flux.

RxJava peut être comparé à CompletableFuture, mais cela permet de calculer plus d'une valeur.

46
dwursteisen

Il existe quelques différences techniques et conceptuelles, par exemple, les flux Java 8 sont des séquences de valeurs synchrones à usage unique, basées sur l'extraction, tandis que RxJava Observables sont des séquences réobservables, adaptables et potentiellement asynchrones. valeurs. RxJava est destiné à Java 6+ et fonctionne également sur Android.

35
akarnokd

Les flux Java 8 sont basés sur l'extraction. Vous parcourez un flux Java 8 consommant chaque élément. Et cela pourrait être un flux sans fin.

RXJava Observable est par défaut basé sur Push. Vous vous abonnez à un observable et vous recevrez une notification lorsque le prochain élément arrivera (onNext), lorsque le flux sera terminé (onCompleted) ou lorsqu’une erreur se produira (onError). Parce que, avec Observable, vous recevez des événements onNext, onCompleted, onError, vous pouvez effectuer des fonctions puissantes telles que la combinaison de différents Observables avec un nouveau (Zip, merge, concat). Vous pouvez également utiliser la mise en cache, la limitation, etc., et utiliser plus ou moins la même API dans différentes langues (RxJava, RX en C #, RxJS, ...)

Par défaut, RxJava est à thread unique. À moins que vous ne commenciez à utiliser les planificateurs, tout se passera sur le même fil.

29
Bart De Neuter

Les réponses existantes sont complètes et correctes, mais il manque un exemple clair pour les débutants. Permettez-moi de mettre des termes concrets derrière des termes tels que "Push/pull-based" et "re-observable". Remarque : Je déteste le terme Observable (c'est un flux pour l'amour du ciel), nous allons donc simplement faire référence aux flux J8 vs RX.

Considérons une liste d'entiers,

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

Un flux J8 est un utilitaire permettant de modifier la collection. Par exemple, même les chiffres peuvent être extraits sous la forme,

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

C’est fondamentalement celui de Python carte, filtre, réduction , un ajout très agréable (et attendu depuis longtemps) à Java. Mais que se passe-t-il si les chiffres ne sont pas collectés à l'avance, si les chiffres sont entrés en continu pendant le fonctionnement de l'application, pouvons-nous filtrer les événements en temps réel?.

Imaginons qu'un processus de thread séparé produise des entiers à des moments aléatoires lorsque l'application est en cours d'exécution (--- indique l'heure)

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

Dans RX, evencan réagit à chaque nouveau chiffre et applique le filtre en temps réel

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

Il n'est pas nécessaire de stocker des listes d'entrée et de sortie. Si vous souhaitez une liste de sortie, aucun problème ne peut être streamé. En fait, tout est un flux.

evens_stored = even.collect()  

C’est pourquoi des termes tels que "sans état" et "fonctionnel" sont plus associés à RX

25
Adam Hughes

RxJava est également étroitement lié à initiative de flux réactifs et se considère lui-même comme une simple implémentation de l'API de flux réactifs (par exemple, comparé à implémentation de flux Akka ). La principale différence est que les flux réactifs sont conçus pour pouvoir gérer la contre-pression, mais si vous consultez la page Flux réactifs, vous aurez une idée. Ils décrivent assez bien leurs objectifs et les flux sont aussi étroitement liés à le manifeste réactif .

Les flux Java 8 sont en gros l’implémentation d’une collection illimitée, assez semblable au flux Scala Stream ou au Clojure lazy seq .

4
Niclas Meier

Les flux Java 8 permettent de traiter efficacement de très grandes collections tout en exploitant les architectures multicœurs. En revanche, RxJava est mono-thread par défaut (sans planificateurs). Donc, RxJava ne tirera pas parti des machines multicœurs si vous ne codez pas cette logique vous-même.

2
IgorGanapolsky