5. #J8Stream @JosePaumard
Traitement de donnéesTraitement de données
• De 1998 à 2014… seul outil : l’API Collection
Common collections
• À partir de 2014… un foisonnement !
6. #J8Stream @JosePaumard
Traitement de donnéesTraitement de données
• Pourquoi autant d’offres ?
• Parce que le traitement de données devient central…
7. #J8Stream @JosePaumard
Traitement de donnéesTraitement de données
• Pourquoi autant d’offres ?
• Parce que le traitement de données devient central…
et complexe !
8. #J8Stream @JosePaumard
Traitement de donnéesTraitement de données
• Pourquoi autant d’offres ?
• Parce que le traitement de données devient central…
et complexe !
Volumes de plus en plus importants
9. #J8Stream @JosePaumard
Traitement de donnéesTraitement de données
• Pourquoi autant d’offres ?
• Parce que le traitement de données devient central…
et complexe !
Volumes de plus en plus importants
Temps de réponse maîtrisés
10. #J8Stream @JosePaumard
Traitement de donnéesTraitement de données
• Pourquoi autant d’offres ?
• Parce que le traitement de données devient central…
et complexe !
Volumes de plus en plus importants
Temps de réponse maîtrisés
Algorithmes complexes
12. #J8Stream @JosePaumard
Traitement de donnéesTraitement de données
• Le traitement de données a besoin de « primitives »
de haut niveau
Qui permettent d’accéder aux données où qu’elles
soient
13. #J8Stream @JosePaumard
Traitement de donnéesTraitement de données
• Le traitement de données a besoin de « primitives »
de haut niveau
Qui permettent d’accéder aux données où qu’elles
soient
Qui exposent des fonctions (map / filter / reduce)
14. #J8Stream @JosePaumard
Traitement de donnéesTraitement de données
• Le traitement de données a besoin de « primitives »
de haut niveau
Qui permettent d’accéder aux données où qu’elles
soient
Qui exposent des fonctions (map / filter / reduce)
Qui soient efficaces !
15. #J8Stream @JosePaumard
Traitement de donnéesTraitement de données
• Le traitement de données a besoin de « primitives »
de haut niveau
Qui permettent d’accéder aux données où qu’elles
soient
Qui exposent des fonctions (map / filter / reduce)
Qui soient efficaces !
Éventuellement parallèles
16. #J8Stream @JosePaumard
Objet de la présentationObjet de la présentation
• Présenter trois API
Exposer les concepts fondamentaux
Présenter les fonctions implémentées
Montrer des patterns de code (Java 8, lambdas)
• Comparer ces API
Point de vue de l’utilisateur
Performances !
17. #J8Stream @JosePaumard
Objet de la présentationObjet de la présentation
• Proposer une grille de lecture et d’analyse
Comparer des outils qui proposent des solutions
différentes pour un même problème
18. #J8Stream @JosePaumard
Objet de la présentationObjet de la présentation
• Proposer une grille de lecture et d’analyse
Comparer des outils qui proposent des solutions
différentes pour un même problème
• Du point de vue du développeur
19. #J8Stream @JosePaumard
Objet de la présentationObjet de la présentation
• Proposer une grille de lecture et d’analyse
Comparer des outils qui proposent des solutions
différentes pour un même problème
• Du point de vue du développeur
Performance
Patterns écriture / lecture
28. #J8Stream @JosePaumard
Les forces en présenceLes forces en présence
• Les 3 API sont :
L’API Stream de Java 8
GS Collections
RxJava
29. #J8Stream @JosePaumard
Les forces en présenceLes forces en présence
• Les 3 API sont :
L’API Stream de Java 8
GS Collections
RxJava
• Comparaison des performances sur un exemple
commun
32. #J8Stream @JosePaumard
API StreamAPI Stream
• Qu’est-ce qu’un Stream Java 8 ?
Un objet que l’on connecte à une source
Un objet qui ne porte pas les données qu’il traite
33. #J8Stream @JosePaumard
API StreamAPI Stream
• Qu’est-ce qu’un Stream Java 8 ?
Un objet que l’on connecte à une source
Un objet qui ne porte pas les données qu’il traite
Un objet qui expose le pattern map / filter / reduce
34. #J8Stream @JosePaumard
API StreamAPI Stream
• Qu’est-ce qu’un Stream Java 8 ?
Un objet que l’on connecte à une source
Un objet qui ne porte pas les données qu’il traite
Un objet qui expose le pattern map / filter / reduce
• Nouveau concept introduit dans le JDK
35. #J8Stream @JosePaumard
API StreamAPI Stream
• Exemple
List<String> liste = Arrays.asList("un", "deux", "trois") ;
liste.stream()
.map(s ‐> s.toUpperCase())
.max(Comparator.comparing(s ‐> s.length()))
.ifPresent(s ‐> System.out.println(s)) ;
36. #J8Stream @JosePaumard
API StreamAPI Stream
• Exemple
List<String> liste = Arrays.asList("un", "deux", "trois") ;
liste.stream() // ouverture d’un stream de String
.map(s ‐> s.toUpperCase())
.max(Comparator.comparing(s ‐> s.length()))
.ifPresent(s ‐> System.out.println(s)) ;
37. #J8Stream @JosePaumard
API StreamAPI Stream
• Exemple
List<String> liste = Arrays.asList("un", "deux", "trois") ;
liste.stream()
.map(s ‐> s.toUpperCase()) // mise en majuscules des éléments
.max(Comparator.comparing(s ‐> s.length()))
.ifPresent(s ‐> System.out.println(s)) ;
38. #J8Stream @JosePaumard
API StreamAPI Stream
• Exemple
List<String> liste = Arrays.asList("un", "deux", "trois") ;
liste.stream()
.map(s ‐> s.toUpperCase())
.max(Comparator.comparing(s ‐> s.length())) // plus longue
.ifPresent(s ‐> System.out.println(s)) ;
39. #J8Stream @JosePaumard
API StreamAPI Stream
• Exemple
List<String> liste = Arrays.asList("un", "deux", "trois") ;
liste.stream()
.map(s ‐> s.toUpperCase())
.max(Comparator.comparing(s ‐> s.length()))
.ifPresent(s ‐> System.out.println(s)) ; // Optional !
40. #J8Stream @JosePaumard
API StreamAPI Stream
• Exemple
List<String> liste = Arrays.asList("un", "deux", "trois") ;
liste.stream()
.map(String::toUpperCase)
.max(Comparator.comparing(String::length)
.ifPresent(System.out::println) ; // Optional !
41. #J8Stream @JosePaumard
CollectorsCollectors
• On peut collecter des données
List<Person> liste = ... ;
liste.stream()
.filter(person ‐> person.getAge() > 30)
.collect(
Collectors.groupingBy(
Person::getAge, // key extractor
Collectors.counting() // downstream collector
)
) ;
42. #J8Stream @JosePaumard
CollectorsCollectors
• On peut collecter des données
List<Person> liste = ... ;
liste.stream()
.filter(person ‐> person.getAge() > 30)
.collect(
Collectors.groupingBy(
Person::getAge, // key extractor
Collectors.counting() // downstream collector
)
) ;
43. #J8Stream @JosePaumard
CollectorsCollectors
• On peut collecter des données
List<Person> liste = ... ;
Map<Integer, Long> map =
liste.stream()
.filter(person ‐> person.getAge() > 30)
.collect(
Collectors.groupingBy(
Person::getAge, // key extractor
Collectors.counting() // downstream collector
)
) ;
44. #J8Stream @JosePaumard
CollectorsCollectors
• Et on peut paralléliser les traitements
List<Person> liste = ... ;
Map<Integer, Long> map =
liste.stream().parallel()
.filter(person ‐> person.getAge() > 30)
.collect(
Collectors.groupingBy(
Person::getAge, // key extractor
Collectors.counting() // downstream collector
)
) ;
46. #J8Stream @JosePaumard
Particularisation d’un StreamParticularisation d’un Stream
• On peut le connecter à sa propre source de données
Implémenter un Spliterator (~ Iterator)
• On peut implémenter son propre Collector
49. #J8Stream @JosePaumard
GS CollectionsGS Collections
• Une alternative à l’API Collection
Parfois très ressemblante…
• Des interfaces supplémentaires
Beaucoup !
Des méthodes supplémentaires sur les interfaces
existantes
56. #J8Stream @JosePaumard
GS CollectionsGS Collections
• Nouveaux concepts ?
• Plutôt des extensions des concepts de
l’API Collection
Certains sont dans Common collections ou Guava
80. #J8Stream @JosePaumard
BagBag
• Méthodes de réduction
Bag<Person> bag = ... ;
Person first = bag.detect(
person ‐> person.getAge() > 30) ;
Person p = bag.detectIfNone(
person ‐> person.getAge() > 100,
() ‐> Person.NOBODY) ;
81. #J8Stream @JosePaumard
BagBag
• Méthodes de réduction
Bag<Person> bag = ... ;
Person first = bag.detectWith(
(person, param) ‐> person.getAge() > param,
30) ;
Person p = bag.detectWithIfNone(
(person, param) ‐> person.getAge() > param,
100,
() ‐> Person.NOBODY) ;
82. #J8Stream @JosePaumard
BagBag
• Méthodes de folding
Bag<Person> bag = ... ;
long sum = bag.injectInto(
0L,
(sum, person) ‐> sum + person.getAge()) ;
long count = bag.injectInto(
0L,
(sum, person) ‐> sum + 1L) ;
83. #J8Stream @JosePaumard
BagBag
• Méthodes de folding
Bag<Person> bag = ... ;
long sum = bag.injectInto(
0L,
(l, person) ‐> l + person.getAge()) ;
long sumOfAge = bag.sumOfInt(Person::getAge)) ;
84. #J8Stream @JosePaumard
BagBag
• Méthodes de folding
Bag<Person> bag = ... ;
String names = bag.collect(Person::getName).makeString() ;
Charles, Michelle, Paul, Barbara
85. #J8Stream @JosePaumard
BagBag
• Réduction dans une chaîne de caractères
Bag<Person> bag = ... ;
String names = bag.collect(Person::getAge).makeString() ;
String names = bag.collect(Person::getAge).makeString(" ; ") ;
Charles ; Michelle ; Paul ; Barbara
86. #J8Stream @JosePaumard
BagBag
• Réduction dans une chaîne de caractères
Bag<Person> bag = ... ;
String names = bag.collect(Person::getAge).makeString() ;
String names = bag.collect(Person::getAge).makeString(" ; ") ;
String names = bag.collect(Person::getAge).makeString("{", " ; ", "}") ;
{Charles ; Michelle ; Paul ; Barbara}
90. #J8Stream @JosePaumard
MutableMapMutableMap
• Méthodes de Map
V getIfAbsentPut(K key,
Function0<? extends V> function) ;
V getIfAbsentPutWithKey(K key,
Function<? super K, ? extends V> function) ;
V getIfAbsentPutWith(K key,
Function<? super P, ? extends V> function
P parameter) ;
91. #J8Stream @JosePaumard
MutableMapMutableMap
• Méthodes forEach()
void forEachKeyValue(Procedure2<? super K, ? super V> procedure) ;
void forEachKey(Procedure<? super T> procedure) ;
void forEachValue(Procedure<? super T> procedure) ;
92. #J8Stream @JosePaumard
MutableMapMutableMap
• Méthodes map
• Une table de hachage est aussi une liste de valeurs
MapIterable<K2, V2>
collect(Function2<? super K, ? super V, Pair<K2, V2>> function) ;
public interface MapIterable<K, V> extends RichIterable<V> {
}
93. #J8Stream @JosePaumard
PairPair
• Modélisation d’un tuple
public interface Pair<T1, T2>
extends Serializable, Comparable<Pair<T1, T2>> {
T1 getOne();
T2 getTwo();
void put(Map<T1, T2> map);
Map.Entry<T1, T2> toEntry(); // pont avec API Collection
Pair<T2, T1> swap();
}
94. #J8Stream @JosePaumard
MutableMapMutableMap
• Les méthodes map, filter et méthodes de réduction
sont définies sur RichIterable
Donc disponibles sur les tables de hachage
Elles opèrent sur les valeurs
• Méthodes groupBy, retournent de nouvelles tables
reject(Predicate2<? super K, ? super V> predicate) ;
select(Predicate2<? super K, ? super V> predicate) ;
96. #J8Stream @JosePaumard
MultiMapMultiMap
• Concept qui ne fait pas partie de l’API Collection
• Map qui associe plusieurs valeurs à une même clé
Gère une collection de valeurs en interne
MultiMap<Integer, Person> map ; // GS Collections
Map<Integer, List<Person>> map ; // API Collection
97. #J8Stream @JosePaumard
MultiMapMultiMap
• La plupart des méthodes sont les mêmes
• Méthodes get différentes
RichIterable<V> values = multiMap.get(K key) ;
MutableMap<K, RichIterable<V>> map = multiMap.toMap() ;
99. #J8Stream @JosePaumard
Opérations de type ZipOpérations de type Zip
• Permet d’appairer deux ensembles
Bag<Person> bag1 = ImmutableBagImpl.of(anna, charles) ;
Bag<Person> bag2 = ImmutableBagImpl.of(paul, barbara) ;
Bag<Pair<Person, Person>> friends = bag1.zip(bag2) ;
100. #J8Stream @JosePaumard
Opérations de type ZipOpérations de type Zip
• Permet d’appairer deux ensembles
Bag<Person> bag1 = ImmutableBagImpl.of(anna, charles) ;
Bag<Person> bag2 = ImmutableBagImpl.of(paul, barbara) ;
Bag<City> bag3 = ImmutableBagImpl.of(paris, sanFrancisco) ;
Bag<Pair<Person, Person>> friends = bag1.zip(bag2) ;
Bag<Pair<Person, City>> living = bag1.zip(bag3) ;
101. #J8Stream @JosePaumard
Opérations de type ZipOpérations de type Zip
• Permet d’appairer deux ensembles
Bag<Person> bag1 = ImmutableBagImpl.of(anna, charles) ;
Bag<Person> bag2 = ImmutableBagImpl.of(paul, barbara) ;
Bag<City> bag3 = ImmutableBagImpl.of(paris, sanFrancisco) ;
Bag<Pair<Person, Person>> friends = bag1.zip(bag2) ;
Bag<Pair<Person, City>> living = bag1.zip(bag3) ;
Bag<Pair<City, Integer>> cities = bag3.zipWithIndex() ;
107. #J8Stream @JosePaumard
Bilan sur l’APIBilan sur l’API
• Une API complète (complexe)
• Concept de MultiMap
• Des méthodes que l’on trouve sur Stream
Ajoutées aux collections
• Des méthodes supplémentaires
Que l’on aura peut-être sur Stream (zip)
109. #J8Stream @JosePaumard
RxJavaRxJava
• API Open source, Github
• Développé par Netflix
• Version Java de ReactiveX
.NET
Python, Kotlin, JavaScript, Scala, Ruby, Groovy, Rust
Android
https://github.com/ReactiveX/RxJava
110. #J8Stream @JosePaumard
Vue d’ensembleVue d’ensemble
• Approche différente de GS Collections
• Il ne s’agit pas d’une implémentation alternative de
l’API Collection
• Mais plutôt de l’implémentation du pattern Reactor
114. #J8Stream @JosePaumard
Notions fondamentalesNotions fondamentales
• Observable : source de données
Objet complexe ~100 méthodes statiques + ~150
méthodes non statiques
• Observer : permet d’observer un observable
Objet simple : 3 méthodes
115. #J8Stream @JosePaumard
Notions fondamentalesNotions fondamentales
• Observable : source de données
Objet complexe ~100 méthodes statiques + ~150
méthodes non statiques
• Observer : permet d’observer un observable
Objet simple : 3 méthodes
• Subscription : lien qui existe entre un observable et
un observer
119. #J8Stream @JosePaumard
Création d’un ObservableCréation d’un Observable
• Constructeur non vide protégé
• On peut construire un Observable
par extension
en utilisant une des méthodes statiques
120. #J8Stream @JosePaumard
Création d’un ObservableCréation d’un Observable
• Constructeur non vide protégé
• On peut construire un Observable
par extension
en utilisant une des méthodes statiques
• Prend un producer en paramètre
121. #J8Stream @JosePaumard
Création d’un ObservableCréation d’un Observable
• Création à partir de collections
Observable<String> obs1 = Observable.just("one", "two", "three") ;
List<String> strings = Arrays.asList("one", "two", "three") ;
Observable<String> obs2 = Observable.from(strings) ;
122. #J8Stream @JosePaumard
Création d’un ObservableCréation d’un Observable
• Création à partir de collections
Observable<String> obs1 = Observable.just("one", "two", "three") ;
List<String> strings = Arrays.asList("one", "two", "three") ;
Observable<String> obs2 = Observable.from(strings) ;
Observable<String> empty = Observable.empty() ;
Observable<String> never = Observable.never() ;
Observable<String> error = Observable.<String>error(exception) ;
123. #J8Stream @JosePaumard
Création d’un ObservableCréation d’un Observable
• Création de séries
Observable<Long> longs = Observable.range(1L, 100L) ;
124. #J8Stream @JosePaumard
Création d’un ObservableCréation d’un Observable
• Création de séries
Observable<Long> longs = Observable.range(1L, 100L) ;
// interval
Observable<Long> timeSerie1 =
Observable.interval(1L, TimeUnit.MILLISECONDS) ; // a serie of longs
// initial delay, then interval
Observable<Long> timeSerie2 =
Observable.timer(10L, 1L, TimeUnit.MILLISECONDS) ; // one 0
125. #J8Stream @JosePaumard
Création d’un ObservableCréation d’un Observable
• Méthode using()
public final static <T, Resource> Observable<T> using(
final Func0<Resource> resourceFactory, // producer
final Func1<Resource, Observable<T>> observableFactory, // function
final Action1<? super Resource> disposeAction // consumer
) { }
126. #J8Stream @JosePaumard
Notion de SchedulerNotion de Scheduler
• Ces méthodes peuvent prendre un autre paramètre
• Scheduler : interface
• Associée à la factory Schedulers
Observable<Long> longs = Observable.range(0L, 100L, scheduler) ;
127. #J8Stream @JosePaumard
Notion de SchedulerNotion de Scheduler
• Factory Schedulers
public final class Schedulers {
public static Scheduler immediate() {...} // immediate, same thread
public static Scheduler newThread() {...} // new thread
public static Scheduler computation() {...} // computation ES
public static Scheduler io() {...} // IO growing ES
}
128. #J8Stream @JosePaumard
Notion de SchedulerNotion de Scheduler
• Factory Schedulers
public final class Schedulers {
public static Scheduler trampoline() {...} // queued in the current
// thread
public static Scheduler test() {...}
public static Scheduler fromExecutor(Executor executor) {...}
}
129. #J8Stream @JosePaumard
Notion de SchedulerNotion de Scheduler
• RxJava permet de créer des Observable dans
différents threads
• Certains pools sont spécialisés : IO, Computation
• On peut passer ses propres pools
• Les observateurs sont appelés dans les threads des
observables
130. #J8Stream @JosePaumard
Notion de SchedulerNotion de Scheduler
• La classe Scheduler n’est pas immédiate à étendre
• On utilise from() pour les threads IHM (Swing,
JavaFX)
134. #J8Stream @JosePaumard
Observable : méthodes statiquesObservable : méthodes statiques
• Série de méthodes statiques de combinaisons
d’Observable en un seul
138. #J8Stream @JosePaumard
Observable de listesObservable de listes
• concat() : émet O1 puis O2, sans les mélanger
• merge() : émet O1 et O2, s’arrête sur erreur
149. #J8Stream @JosePaumard
ExemplesExemples
• Un petit cas simple
Observable<Integer> range1To100 = Observable.range(1L, 100L) ;
manyStrings.subscribe(System.out::println) ;
> 1 2 3 4 ... 100
150. #J8Stream @JosePaumard
ExemplesExemples
• Un petit cas un peu plus dur
Observable<Integer> timer = Observable.timer(1, TimeUnit.SECONDS) ;
timer.subscribe(System.out::println) ;
151. #J8Stream @JosePaumard
ExemplesExemples
• Un petit cas un peu plus dur
• N’affiche rien…
Observable<Integer> timer = Observable.timer(1, TimeUnit.SECONDS) ;
timer.subscribe(System.out::println) ;
>
152. #J8Stream @JosePaumard
ExemplesExemples
• Un petit cas un peu plus dur
Observable<Integer> timer = Observable.timer(1, TimeUnit.SECONDS) ;
timer.subscribe(() ‐> {
System.out.println(Thread.currentThread().getName() + " " +
Thread.currentThread().isDaemon()) ;
}) ;
Thread.sleep(2) ;
153. #J8Stream @JosePaumard
ExemplesExemples
• Un petit cas un peu plus dur
Observable<Integer> timer = Observable.timer(1, TimeUnit.SECONDS) ;
timer.subscribe(() ‐> {
System.out.println(Thread.currentThread().getName() + " " +
Thread.currentThread().isDaemon()) ;
}) ;
Thread.sleep(2) ;
> RxComputationThreadPool‐1 ‐ true
154. #J8Stream @JosePaumard
ExemplesExemples
• Encore un peu plus dur
Observable<Integer> range1To100 = Observable.range(1, 100) ;
Observable<String> manyStrings =
Observable.combineLatest(
range1To100, Observable.just("one"),
(integer, string) ‐> string) ;
155. #J8Stream @JosePaumard
ExemplesExemples
• Encore un peu plus dur
Observable<Integer> range1To100 = Observable.range(1, 100) ;
Observable<String> manyStrings =
Observable.combineLatest(
range1To100, Observable.just("one"),
(integer, string) ‐> string) ;
> one (et c’est tout)
Combines two source Observables
by emitting an item that aggregates
the latest values of each of the
source Observables
each time an item is received from
either of the source Observables,
where this aggregation is defined by
a specified function.
156. #J8Stream @JosePaumard
ExemplesExemples
• Encore un peu plus dur
Observable<Integer> serie = Observable.interval(3, TimeUnit.MILLISECONDS) ;
Observable<String> manyStrings =
Observable.combineLatest(
serie, Observable.just("one"),
(integer, string) ‐> string) ;
157. #J8Stream @JosePaumard
ExemplesExemples
• Encore un peu plus dur
Observable<Integer> serie = Observable.interval(3, TimeUnit.MILLISECONDS) ;
Observable<String> manyStrings =
Observable.combineLatest(
serie, Observable.just("one"),
(integer, string) ‐> string) ;
> one one one one one one ...
158. #J8Stream @JosePaumard
Observables hot & coldObservables hot & cold
• Deux mécanismes d’Observable :
Ceux qui émettent des données lorsqu’elles sont
consommées = cold observables
Ceux qui émettent des données, qu’elles soient
consommées ou pas = hot observables
• Un hot observable peut générer ses données
indépendamment de ses observateurs
166. #J8Stream @JosePaumard
Remarque sur le tempsRemarque sur le temps
• RxJava peut mesurer le temps « en réel »
• Peut aussi utiliser un Observable comme mesure du
temps
Appels à onNext() et onComplete()
167. #J8Stream @JosePaumard
Sélection d’élémentsSélection d’éléments
• Retournent Observable<T>
• Exception en cas de timeout entre deux émissions
first() { }
last() { }
skip(int n) { }
limit(int n) { }
take(int n) { }
timeout(long n, TimeUnit timeUnit) { }
timeout(Func0<Observable<U>> firstTimeoutSelector, // producer
Func1<T, Observable<V>> timeoutSelector) { } // function
172. #J8Stream @JosePaumard
Opérations join / groupJoinOpérations join / groupJoin
• Retourne un Observable<GroupedObservable<K, T>>
• GroupedObservable : observable avec une clé
groupBy(Func1<T, K> keySelector) { } // function
173. #J8Stream @JosePaumard
Opérations join / groupJoinOpérations join / groupJoin
• Combine deux observables, sur un timer
public final <T2, D1, D2, R> Observable<R>
groupJoin(
Observable<T2> right,
Func1<T, Observable<D1>> leftDuration, // function
Func1<T2, Observable<D2>> rightDuration, // function
Func2<T, Observable<T2>, R> resultSelector // bifunction
) { }
174. #J8Stream @JosePaumard
Opérations join / groupJoinOpérations join / groupJoin
• Les fenêtres de temps sont définies par des
observables
Elles démarrent lorsque l’observable démarre
Et se ferment lorsque l’observable émet un objet ou
s’arrête
176. #J8Stream @JosePaumard
Méthodes de debugMéthodes de debug
• Deux méthodes, font du mapping
• Notification : objet qui englobe des meta-données en
plus
materialize() { } // Observable<Notification<T>>
177. #J8Stream @JosePaumard
Méthodes de debugMéthodes de debug
• Deux méthodes, font du mapping
• Notification : objet qui englobe des métadonnées en
plus des objets de l’Observable
materialize() { } // Observable<Notification<T>>
dematerialize() { } // Observable<T>
179. #J8Stream @JosePaumard
Méthodes synchronesMéthodes synchrones
• Associent des opérations avec une horloge
• Ou avec un autre Observable
delay(long delay, TimeUnit timeUnit) ; // Observable<T>
delay(Func1<T, Observable<U> func1) ; // Observable<T>
181. #J8Stream @JosePaumard
Méthodes synchronesMéthodes synchrones
• Limite le nombre d’éléments émis
• Peut aussi se caler sur les événements d’un
Observable
debounce(long delay, TimeUnit timeUnit) ; // Observable<T>
debounce(Func1<T, Observable<U> func1) ; // Observable<T>
182. #J8Stream @JosePaumard
Méthodes synchronesMéthodes synchrones
• Mesure du temps entre événements
• TimeInterval : wrapper pour la durée en ms et la
valeur émise
timeInterval() { } // Observable<TimeInterval<T>>
185. #J8Stream @JosePaumard
Méthodes backpressureMéthodes backpressure
• Problème : une source peut générer « trop »
d’éléments
Par rapport à la vitesse des consommateurs
• Certaines méthodes permettent de « sauter » des
éléments
186. #J8Stream @JosePaumard
Méthodes backpressureMéthodes backpressure
• Problème : une source peut émettre « trop »
d’éléments
Par rapport à la vitesse des consommateurs
• Ne peut pas arriver avec les cold observables
187. #J8Stream @JosePaumard
Méthodes backpressureMéthodes backpressure
• Problème : une source peut émettre « trop »
d’éléments
Par rapport à la vitesse des consommateurs
• Ne peut pas arriver avec les cold observables
• Un observable cold peut devenir hot…
197. #J8Stream @JosePaumard
Méthodes repeat & retryMéthodes repeat & retry
• repeat() : répète l’émission d’objets indéfiniment
• Sur cet Observable, le notification handler peut alors
invoquer :
onComplete() ou onError(), ce qui déclenche le même
appel sur l’Observable source
onNext(), ce qui déclenche la répétition
199. #J8Stream @JosePaumard
Méthodes repeat & retryMéthodes repeat & retry
• retry() : répète l’émission d’objets sur erreur
• Quand l’observable appelle onError(), le notification
handler est invoqué avec l’exception
retry() { } // Observable<T>
retry(long times) { } // Observable<T>
retryWhen(
Func1<Observable<Throwable>>, Observable<?>> notificationHandler
) { }
200. #J8Stream @JosePaumard
Méthodes repeat & retryMéthodes repeat & retry
• Sur cet Observable, le notification handler peut alors
invoquer :
onComplete() ou onError(), ce qui déclenche le même
appel sur l’Observable source
onNext(), ce qui déclenche la répétition
203. #J8Stream @JosePaumard
Plusieurs ObserverPlusieurs Observer
• Cas d’un cold Observable :
La consommation par plusieurs observateurs ne pose
pas de problème
• Cas d’un hot Observable :
Un deuxième observateur peut « manquer » les
premières valeurs
206. #J8Stream @JosePaumard
Plusieurs ObserverPlusieurs Observer
• Notion de ConnectableObserver
• L’enregistrement d’observateurs ne déclenche pas la
production des éléments
Observable<T> observable = ... ;
ConnectableObservable<T> connectable = observable.publish() ;
207. #J8Stream @JosePaumard
Plusieurs ObserverPlusieurs Observer
• Notion de ConnectableObserver
• L’enregistrement d’observateurs ne déclenche pas la
production des éléments
• Pour cela : appeler connect()
Observable<T> observable = ... ;
ConnectableObservable<T> connectable = observable.publish() ;
connectable.publish() ;
208. #J8Stream @JosePaumard
Bilan sur RxJavaBilan sur RxJava
• API Complète (complexe)
• Permet de gérer le lancement de traitements dans
différents pools de threads
209. #J8Stream @JosePaumard
Bilan sur RxJavaBilan sur RxJava
• API Complète (complexe)
• Permet de gérer le lancement de traitements dans
différents pools de threads
• Permet de synchroniser les opérations
Sur une horloge
Sur des références applicatives
211. #J8Stream @JosePaumard
Le meilleur des deux mondes ?Le meilleur des deux mondes ?
• Connecter GS Collections et Java 8 Stream ?
= connecter Stream et Iterable
212. #J8Stream @JosePaumard
Le meilleur des deux mondes ?Le meilleur des deux mondes ?
• Connecter GS Collections et Java 8 Stream ?
= connecter Stream et Iterable
• Connecter Java 8 Stream et RxJava ?
= connecter Stream et Observable
213. #J8Stream @JosePaumard
Spliterator et IteratorSpliterator et Iterator
• Les collections sont construites sur des Iterator
• Les Stream sont construits sur des Spliterator
Iterator<T> iterator = ... ;
Spliterator<T> spliterator =
Spliterators.spliteratorUnknownSize(iterator, 0) ;
Spliterator<T> spliterator =
Spliterators.spliterator(iterator, size, 0) ;
214. #J8Stream @JosePaumard
Spliterator et IteratorSpliterator et Iterator
• Les collections sont construites sur des Iterator
• Les Stream sont construits sur des Spliterator
Spliterator<T> spliterator = ... ;
Iterator<T> iterator = Spliterators.iterator(spliterator) ;
Iterator<T> iterator = ... ;
Iterable<T> iterable = () ‐> iterator ;
215. #J8Stream @JosePaumard
GS Collections & StreamGS Collections & Stream
• Le problème consiste à passer d’une Collection à un
Stream (et réciproquement)
216. #J8Stream @JosePaumard
GS Collections & StreamGS Collections & Stream
• Le problème consiste à passer d’une Collection à un
Stream (et réciproquement)
• Donc d’un Iterator à un Spliterator (et
réciproquement)
217. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Si l’on a un Iterator : facile !
Iterator<T> iterator = ... ;
Observable<T> observable = Observable.from(() ‐> iterator) ;
218. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Si l’on a un Iterator : facile !
• Si l’on a un Spliterator : facile !
Iterator<T> iterator = ... ;
Observable<T> observable = Observable.from(() ‐> iterator) ;
Spliterator<T> spliterator = ... ;
Observable<T> observable =
Observable.from(() ‐> Spliterators.iterator(spliterator)) ;
219. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Donc si l’on a un Stream, on peut facilement
construire un Observable
220. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Donc si l’on a un Stream, on peut facilement
construire un Observable
• L’inverse ?
221. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Donc si l’on a un Stream, on peut facilement
construire un Observable
• L’inverse ?
On peut construire un Iterator sur un Observable
Puis un Spliterator sur un Iterator
222. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Implémenter un Iterator :
Deux méthodes next() et hasNext()
La méthode remove() et une méthode par défaut
223. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Un Iterator « tire » les données d’une source
• Alors qu’un Observable « pousse » les données vers
des callbacks
224. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Un Iterator « tire » les données d’une source
• Alors qu’un Observable « pousse » les données vers
des callbacks
• Il nous faut donc une adaptation entre les deux
225. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• On peut s’inspirer du JDK
public static<T> Iterator<T>
iterator(Spliterator<? extends T> spliterator) {
class Adapter implements Iterator<T>, Consumer<T> {
// implementation
}
return new Adapter() ;
}
226. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• On peut s’inspirer du JDK
class Adapter implements Iterator<T>, Consumer<T> {
boolean valueReady = false ;
T nextElement;
public void accept(T t) {
valueReady = true ;
nextElement = t ;
}
public boolean hasNext() {
if (!valueReady)
spliterator.tryAdvance(this) ; // calls accept()
return valueReady ;
}
public T next() {
if (!valueReady && !hasNext())
throw new NoSuchElementException() ;
else {
valueReady = false ;
return nextElement ;
}
}
}
227. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• On peut s’inspirer du JDK
public static<T> Iterator<T>
of(Observable<? extends T> obsevable) {
class Adapter implements Iterator<T> {
// implementation
}
return new Adapter() ;
}
228. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• On peut s’inspirer du JDK
class Adapter implements Iterator<T>, Consumer<T> {
boolean valueReady = false ;
T nextElement;
public void accept(T t) {
valueReady = true ;
nextElement = t ;
}
public boolean hasNext() {
return valueReady ;
}
public T next() {
if (!valueReady && !hasNext())
throw new NoSuchElementException() ;
else {
valueReady = false ;
return nextElement ;
}
}
}
229. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• On peut s’inspirer du JDKclass Adapter implements Iterator<T>, Consumer<T> {
boolean valueReady = false ;
T nextElement;
public void accept(T t) {
observable.subscribe(
element ‐> nextElement = element, // onNext
exception ‐> valueReady = false, // onError
() ‐> valueReady = false // onComplete
) ;
}
// next
// hasNext
}
230. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• On peut s’inspirer du JDKclass Adapter implements Iterator<T>, Consumer<T> {
AtomicBoolean valueReady = new AtomicBoolean(false) ;
AtomicReference<T> nextElement = new AtomicReference() ;
public void accept(T t) {
observable.subscribe(
element ‐> nextElement.set(element), // onNext
exception ‐> valueReady.set(false), // onError
() ‐> valueReady.set(false) // onComplete
) ;
}
// next
// hasNext
}
231. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Peut mieux faire ?
interface Wrapper<E> {
E get() ;
}
Wrapper<Boolean> wb = () ‐> true ;
232. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Peut mieux faire ?
interface Wrapper<E> {
E get() ;
}
Wrapper<Boolean> wb = () ‐> true ;
Action1<Boolean> onNext = b ‐> wb.set(b) ; // should return Wrapper<T>
233. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Peut mieux faire ?
interface Wrapper<E> {
E get() ;
public default Wrapper<E> set(E e) {
// should return e
}
}
Wrapper<Boolean> wb = () ‐> true ;
Action1<Boolean> onNext = b ‐> wb.set(b) ; // should return Wrapper<T>
234. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• Peut mieux faire ?
interface Wrapper<E> {
E get() ;
public default Wrapper<E> set(E e) {
return () ‐> e ;
}
}
Wrapper<Boolean> wb = () ‐> true ;
Action1<Boolean> onNext = b ‐> wb.set(b) ; // should return Wrapper<T>
235. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• On peut s’inspirer du JDKclass Adapter implements Iterator<T>, Consumer<T> {
Wrapper<Boolean> valueReady = () ‐> false ;
Wrapper<T> nextElement ;
public void accept(T t) {
observable.subscribe(
element ‐> nextElement.set(element), // onNext
exception ‐> valueReady.set(false), // onError
() ‐> valueReady.set(false) // onComplete
) ;
}
// next
// hasNext
}
236. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• On peut construire un Iterator sur un Observable
Et donc un Spliterator sur un Observable
237. #J8Stream @JosePaumard
RxJava & StreamRxJava & Stream
• On peut construire un Iterator sur un Observable
Et donc un Spliterator sur un Observable
• Fonctionne sur les cold Observable
258. #J8Stream @JosePaumard
Stream vs RxJavaStream vs RxJava
• Pour la partie cold : on peut l’implémenter avec des
Stream
• Pour la partie hot : on peut interfacer les deux API
261. #J8Stream @JosePaumard
Shakespeare joue an ScrabbleShakespeare joue an Scrabble
• « Shakespeare joue au Scrabble »
• Ensemble de mots
Les mots utilisés par Shakespeare
Les mots autorisés au Scrabble
• Question : quel aurait été le meilleur mot que
Shakespeare aurait pu jouer ?
262. #J8Stream @JosePaumard
Shakespeare joue an ScrabbleShakespeare joue an Scrabble
• Comparaison des principaux patterns
• Puis comparaison des performances globales
263. #J8Stream @JosePaumard
Histogramme des lettresHistogramme des lettres
• Java Stream GS Collections Rx Java
// Histogram of the letters in a given word
Function<String, Map<Integer, Long>> histoOfLetters =
word ‐> word.chars().boxed()
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
) ;
264. #J8Stream @JosePaumard
Histogramme des lettresHistogramme des lettres
• Java Stream GS Collections Rx Java
// Histogram of the letters in a given word
Function<String, MutableMap<Integer, Long>> histoOfLetters =
word ‐> new CharArrayList(word.toCharArray())
.collect(c ‐> new Integer((int)c))
// .groupBy(letter ‐> letter) ;
.aggregateBy(
letter ‐> letter,
() ‐> 0L,
(value, letter) ‐> { return value + 1 ; }
) ;
265. #J8Stream @JosePaumard
Histogramme des lettresHistogramme des lettres
• Java Stream GS Collections Rx Java
// Histogram of the letters in a given word
Func1<String, Observable<HashMap<Integer, LongWrapper>>> histoOfLetters =
word ‐> toIntegerObservable.call(word)
.collect(
() ‐> new HashMap<Integer, LongWrapper>(),
(HashMap<Integer, LongWrapper> map, Integer value) ‐> {
LongWrapper newValue = map.get(value) ;
if (newValue == null) {
newValue = () ‐> 0L ;
}
map.put(value, newValue.incAndSet()) ;
}) ;
266. #J8Stream @JosePaumard
Histogramme des lettresHistogramme des lettres
• Java Stream GS Collections Rx Java
interface LongWrapper {
long get() ;
public default LongWrapper set(long l) {
return () ‐> l ;
}
public default LongWrapper incAndSet() {
return () ‐> get() + 1L ;
}
public default LongWrapper add(LongWrapper other) {
return () ‐> get() + other.get() ;
}
}
267. #J8Stream @JosePaumard
Nombre de blancs pour une lettreNombre de blancs pour une lettre
• Java Stream GS Collections Rx Java
// number of blanks for a given letter
ToLongFunction<Map.Entry<Integer, Long>> blank =
entry ‐>
Long.max(
0L,
entry.getValue() ‐
scrabbleAvailableLetters[entry.getKey() ‐ 'a']
) ;
268. #J8Stream @JosePaumard
Nombre de blancs pour une lettreNombre de blancs pour une lettre
• Java Stream GS Collections Rx Java
// number of blanks for a given letter
LongFunction<Map.Entry<Integer, Long>> blank =
entry ‐>
Long.max(
0L,
entry.getValue() ‐
scrabbleAvailableLetters[entry.getKey() ‐ 'a']
) ;
269. #J8Stream @JosePaumard
Nombre de blancs pour une lettreNombre de blancs pour une lettre
• Java Stream GS Collections Rx Java
// number of blanks for a given letter
Func1<Entry<Integer, LongWrapper>, Observable<Long>> blank =
entry ‐>
Observable.just(
Long.max(
0L,
entry.getValue().get() ‐
scrabbleAvailableLetters[entry.getKey() ‐ 'a']
270. #J8Stream @JosePaumard
Nombre de blancs pour un motNombre de blancs pour un mot
• Java Stream GS Collections Rx Java
// number of blanks for a given word
Function<String, Long> nBlanks =
word ‐> histoOfLetters.apply(word)
.entrySet().stream()
.mapToLong(blank)
.sum();
271. #J8Stream @JosePaumard
Nombre de blancs pour un motNombre de blancs pour un mot
• Java Stream GS Collections Rx Java
// number of blanks for a given word
Function<String, Long> nBlanks =
word ‐> UnifiedSet.newSet(
histoOfLetters.valueOf(word)
.entrySet()
)
.sumOfLong(blank) ;
272. #J8Stream @JosePaumard
Nombre de blancs pour un motNombre de blancs pour un mot
• Java Stream GS Collections Rx Java
// number of blanks for a given word
Func1<String, Observable<Long>> nBlanks =
word ‐> histoOfLetters.call(word)
.flatMap(map ‐> Observable.from(() ‐> map.entrySet().iterator()))
.flatMap(blank)
.reduce(Long::sum) ;
273. #J8Stream @JosePaumard
Prédicat sur 2 blancsPrédicat sur 2 blancs
• Java Stream GS Collections Rx Java
// can a word be written with 2 blanks?
Predicate<String> checkBlanks = word ‐> nBlanks.apply(word) <= 2 ;
// can a word be written with 2 blanks?
Predicate<String> checkBlanks = word ‐> nBlanks.valueOf(word) <= 2 ;
// can a word be written with 2 blanks?
Func1<String, Observable<Boolean>> checkBlanks =
word ‐> nBlanks.call(word)
.flatMap(l ‐> Observable.just(l <= 2L)) ;
274. #J8Stream @JosePaumard
Bonus lettre doublée – 1Bonus lettre doublée – 1
• Java Stream GS Collections Rx Java
// Placing the word on the board
// Building the streams of first and last letters
Function<String, IntStream> first3 =
word ‐> word.chars().limit(3);
275. #J8Stream @JosePaumard
Bonus lettre doublée – 1Bonus lettre doublée – 1
• Java Stream GS Collections Rx Java
// Placing the word on the board
// Building the streams of first and last letters
Function<String, MutableList<Integer>> first3 =
word ‐> new CharArrayList(word.toCharArray())
.collect(c ‐> (int)c) ;
.subList(0, Integer.min(list.size(), 3)) ;
276. #J8Stream @JosePaumard
Bonus lettre doublée – 1Bonus lettre doublée – 1
• Java Stream GS Collections Rx Java
// Placing the word on the board
// Building the streams of first and last letters
Func1<String, Observable<Integer>> first3 =
word ‐>
Observable.from(
IterableSpliterator.of(
word.chars().boxed().limit(3).spliterator()
)
) ;
280. #J8Stream @JosePaumard
Score d’un motScore d’un mot
• Java Stream GS Collections Rx Java
// score of the word put on the board
Function<String, Integer> score3 =
word ‐>
2*(score2.apply(word)
+ bonusForDoubleLetter.applyAsInt(word))
+ (word.length() == 7 ? 50 : 0);
281. #J8Stream @JosePaumard
Score d’un motScore d’un mot
• Java Stream GS Collections Rx Java
// score of the word put on the board
Function<String, Integer> score3 =
word ‐>
2*(score2.valueOf(word)
+ bonusForDoubleLetter.intValueOf(word))
+ (word.length() == 7 ? 50 : 0);
282. #J8Stream @JosePaumard
Score d’un motScore d’un mot
• Java Stream GS Collections Rx Java
// score of the word put on the board
Func1<String, Observable<Integer>> score3 =
word ‐>
Observable.just(
score2.call(word), score2.call(word),
bonusForDoubleLetter.call(word), bonusForDoubleLetter.call(word),
Observable.just(word.length() == 7 ? 50 : 0)
)
.flatMap(observable ‐> observable)
.reduce(Integer::sum) ;
291. #J8Stream @JosePaumard
1er bilan : patterns1er bilan : patterns
• Java 8 Stream donne les patterns les plus simples
• Java 8 Stream & GS Collections se ressemblent
292. #J8Stream @JosePaumard
1er bilan : patterns1er bilan : patterns
• Java 8 Stream donne les patterns les plus simples
• Java 8 Stream & GS Collections se ressemblent
• RxJava fait le choix du « tout flatMap » ce qui mène à
des patterns inutilement lourds
293. #J8Stream @JosePaumard
PerformancesPerformances
• Utilisation de JMH
• Outil standard de mesure de performances du JDK
• Développé dans le cadre de l’Open JDK
• Aleksey Shipilev http://shipilev.net/
• https://twitter.com/shipilev
http://openjdk.java.net/projects/code-tools/jcstress/
http://openjdk.java.net/projects/code-tools/jmh/
https://www.parleys.com/tutorial/java-microbenchmark-harness-the-lesser-two-evils
294. #J8Stream @JosePaumard
PerformancesPerformances
• Utilisation de JMH
• Outil standard de mesure de performances du JDK
• Développé dans le cadre de l’Open JDK
• Aleksey Shipilev http://shipilev.net/
• https://twitter.com/shipilev
http://openjdk.java.net/projects/code-tools/jcstress/
http://openjdk.java.net/projects/code-tools/jmh/
https://www.parleys.com/tutorial/java-microbenchmark-harness-the-lesser-two-evils
Aleksey Shipilëv @shipilev
Чувак из ТВ-службы пришёл отключать
антенну. Оказался масс-спектрометристом,
сцепился языком с тестем: стоят, обсуждают
девайсы. #наукоград
299. #J8Stream @JosePaumard
JMHJMH
• 3 façons de mesurer les performances
Temps moyen d’exécution
Nombre d’exécutions par seconde
Échantillonnage (diagramme par quantiles)
306. #J8Stream @JosePaumard
ConclusionConclusion
• La programmation fonctionnelle est « à la mode »
• Du point de vue performance, les choses ne sont pas
si simples
• Le choix de l’API Stream : « une dose de fonctionnel »
est probablement le bon
307. #J8Stream @JosePaumard
ConclusionConclusion
• GS Collections : bonne API
Beaucoup de patterns en doublon des Streams
Compatible Java 7
• RxJava : API riche et complexe
Approche différente, patterns complémentaires
Attention aux performances