SlideShare a Scribd company logo
@JosePaumard
RxJava
J8 Stream
Comparison:
patterns
performances
Why should we be
interested in the
Streams API?
Java 8 Streams and Rx Java Comparison
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
Up to 2014: only one tool, the Collection Framework
Also 3rd party API: Common Collections, …
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
Up to 2014: only one tool, the Collection Framework
Also 3rd party API: Common Collections, …
From 2014: so many new APIs
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
Why so?
Because data processing is more and more important
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
Why so?
Because data processing is more and more important
and more and more complex!
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
Why so?
Because data processing is more and more important
and more and more complex!
bigger and bigger amount of data to process
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
Why so?
Because data processing is more and more important
and more and more complex!
bigger and bigger amount of data to process
controlled response time
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
Why so?
Because data processing is more and more important
and more and more complex!
bigger and bigger amount of data to process
controlled response time
complex algorithms
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
So data processing needs high level primitives
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
So data processing needs high level primitives
Able to access data wherever it is
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
So data processing needs high level primitives
Able to access data wherever it is
That provides map / filter / reduce functions
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
So data processing needs high level primitives
Able to access data wherever it is
That provides map / filter / reduce functions
And efficient implementations of those!
@JosePaumard#Devoxx #J8Stream
It’s all about data processing
So data processing needs high level primitives
Able to access data wherever it is
That provides map / filter / reduce functions
And efficient implementations of those!
Most probably implemented in parallel
@JosePaumard#Devoxx #J8Stream
Agenda
1) To present two API: the Java 8 Stream API and RxJava
• Fundamentals
• Implemented functionalities
• Patterns!
@JosePaumard#Devoxx #J8Stream
Agenda
1) To present two API: the Java 8 Stream API and RxJava
• Fundamentals
• Implemented functionalities
• Patterns!
2) And to compare those APIs
• From the developer point of view
• Performances!
@JosePaumard
@JosePaumard
@JosePaumard#Devoxx #J8Stream
Questions?
#J8Stream
Java 8
Stream API
@JosePaumard#Devoxx #J8Stream
API Stream
What is a Java 8 Stream?
An object that connects to a source
@JosePaumard#Devoxx #J8Stream
API Stream
What is a Java 8 Stream?
An object that connects to a source
That does not hold any data
@JosePaumard#Devoxx #J8Stream
API Stream
What is a Java 8 Stream?
An object that connects to a source
That does not hold any data
That implements the map / filter / reduce pattern
@JosePaumard#Devoxx #J8Stream
API Stream
What is a Java 8 Stream?
An object that connects to a source
That does not hold any data
That implements the map / filter / reduce pattern
A new concept in JDK 8
@JosePaumard#Devoxx #J8Stream
Definition of a Stream
Two things about streams:
1) A Stream does not hold any data
2) A Stream does not modify the data it gets from the source
@JosePaumard#Devoxx #J8Stream
API Stream
Example
List<String> list = Arrays.asList("one", "two", "three") ;
list.stream()
.map(s -> s.toUpperCase())
.max(Comparator.comparing(s -> s.length()))
.ifPresent(s -> System.out.println(s)) ;
@JosePaumard#Devoxx #J8Stream
API Stream
Example
List<String> list = Arrays.asList("one", "two", "three") ;
list.stream() // creation of a new Stream object
.map(s -> s.toUpperCase())
.max(Comparator.comparing(s -> s.length()))
.ifPresent(s -> System.out.println(s)) ;
@JosePaumard#Devoxx #J8Stream
API Stream
Example
List<String> list = Arrays.asList("one", "two", "three") ;
list.stream()
.map(s -> s.toUpperCase()) // to upper case
.filter(s -> s.length() < 20)
.max(Comparator.comparing(s -> s.length()))
.ifPresent(s -> System.out.println(s)) ;
@JosePaumard#Devoxx #J8Stream
API Stream
Example
List<String> list = Arrays.asList("one", "two", "three") ;
list.stream()
.map(s -> s.toUpperCase())
.filter(s -> s.length() < 20) // remove strings longer than 20
.max(Comparator.comparing(s -> s.length()))
.ifPresent(s -> System.out.println(s)) ;
@JosePaumard#Devoxx #J8Stream
API Stream
Example
List<String> list = Arrays.asList("one", "two", "three") ;
list.stream()
.map(s -> s.toUpperCase())
.filter(s -> s.length() < 20)
.max(Comparator.comparing(s -> s.length())) // take the longest s
.ifPresent(s -> System.out.println(s)) ;
@JosePaumard#Devoxx #J8Stream
API Stream
Example
List<String> list = Arrays.asList("one", "two", "three") ;
list.stream()
.map(s -> s.toUpperCase())
.filter(s -> s.length() < 20)
.max(Comparator.comparing(s -> s.length()))
.ifPresent(s -> System.out.println(s)) ; // and print the result
@JosePaumard#Devoxx #J8Stream
API Stream
Example
List<String> list = Arrays.asList("one", "two", "three") ;
list.stream()
.map(String::toUpperCase)
.filter(s -> s.length() < 20)
.max(Comparator.comparing(String::length))
.ifPresent(System.out::println) ; // and print the result
@JosePaumard#Devoxx #J8Stream
Collectors
We can use collectors
List<Person> list = ... ;
list.stream()
.filter(person -> person.getAge() > 30)
.collect(
Collectors.groupingBy(
Person::getAge, // key extractor
Collectors.counting() // downstream collector
)
) ;
@JosePaumard#Devoxx #J8Stream
Collectors
We can use collectors
List<Person> list = ... ;
Map<
list.stream()
.filter(person -> person.getAge() > 30)
.collect(
Collectors.groupingBy(
Person::getAge, // key extractor
Collectors.counting() // downstream collector
)
) ;
@JosePaumard#Devoxx #J8Stream
Collectors
We can use collectors
List<Person> list = ... ;
Map<Integer,
list.stream()
.filter(person -> person.getAge() > 30)
.collect(
Collectors.groupingBy(
Person::getAge, // key extractor
Collectors.counting() // downstream collector
)
) ;
@JosePaumard#Devoxx #J8Stream
Collectors
We can use collectors
List<Person> list = ... ;
Map<Integer, Long> map =
list.stream()
.filter(person -> person.getAge() > 30)
.collect(
Collectors.groupingBy(
Person::getAge, // key extractor
Collectors.counting() // downstream collector
)
) ;
@JosePaumard#Devoxx #J8Stream
Collectors
And we can go parallel!
List<Person> list = ... ;
Map<Integer, Long> map =
list.stream().parallel()
.filter(person -> person.getAge() > 30)
.collect(
Collectors.groupingBy(
Person::getAge, // key extractor
Collectors.counting() // downstream collector
)
) ;
@JosePaumard#Devoxx #J8Stream
Sources of a Stream
Many ways of connecting a Stream to a source of data
@JosePaumard#Devoxx #J8Stream
Sources of a Stream
First patterns to create a Stream
List<Person> people = Arrays.asList(p1, p2, p3);
@JosePaumard#Devoxx #J8Stream
Sources of a Stream
First patterns to create a Stream
List<Person> people = Arrays.asList(p1, p2, p3);
Stream<Person> stream = people.stream();
@JosePaumard#Devoxx #J8Stream
Sources of a Stream
First patterns to create a Stream
List<Person> people = Arrays.asList(p1, p2, p3);
Stream<Person> stream = people.stream();
Stream<Person> stream = Stream.of(p1, p2, p3);
@JosePaumard#Devoxx #J8Stream
More patterns
Stream on String
String crazy = "supercalifragilisticexpialidocious";
IntStream letters = crazy.chars();
long count = letters.distinct().count();
@JosePaumard#Devoxx #J8Stream
More patterns
Stream on String
String crazy = "supercalifragilisticexpialidocious";
IntStream letters = crazy.chars();
long count = letters.distinct().count();
> count = 15
@JosePaumard#Devoxx #J8Stream
More patterns
Stream on String
String crazy = "supercalifragilisticexpialidocious";
IntStream letters = crazy.chars();
letters.boxed().collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
);
@JosePaumard#Devoxx #J8Stream
More patterns
Stream on String
String crazy = "supercalifragilisticexpialidocious";
IntStream letters = crazy.chars();
Map<
letters.boxed().collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
);
@JosePaumard#Devoxx #J8Stream
More patterns
Stream on String
String crazy = "supercalifragilisticexpialidocious";
IntStream letters = crazy.chars();
Map<Integer,
letters.boxed().collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
);
@JosePaumard#Devoxx #J8Stream
More patterns
Stream on String
String crazy = "supercalifragilisticexpialidocious";
IntStream letters = crazy.chars();
Map<Integer, Long> map =
letters.boxed().collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
);
@JosePaumard#Devoxx #J8Stream
More patterns
Stream on String
String crazy = "supercalifragilisticexpialidocious";
IntStream letters = crazy.chars();
Map<Integer, Long> map =
letters.boxed().collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
);
a -> 3
c -> 3
d -> 1
e -> 2
f -> 1
g -> 1
i -> 7
l -> 3
o -> 2
p -> 2
r -> 2
s -> 3
t -> 1
u -> 2
x -> 1
@JosePaumard#Devoxx #J8Stream
More patterns
Stream built on the lines of a file
String book = "alice-in-wonderland.txt";
Stream<String> lines = Files.lines(Paths.get(book)); // autocloseable
@JosePaumard#Devoxx #J8Stream
More patterns
Stream built on the lines of a file
Stream built the words of a String
String book = "alice-in-wonderland.txt";
Stream<String> lines = Files.lines(Paths.get(book)); // autocloseable
String line = "Alice was beginning to get very tired of";
Stream<String> words = Pattern.compile(" ").splitAsStream(line);
@JosePaumard#Devoxx #J8Stream
Flatmap
How to mix both to get all the words of a book?
Memory-efficient implementations built on lazy reading of the
source of data
Function<String, Stream<String>> splitToWords =
line -> Pattern.compile(" ").splitAsStream(line);
Stream<String> lines = Files.lines(path);
Stream<String> words = lines.flatMap(splitToWords);
@JosePaumard#Devoxx #J8Stream
Flatmap
Analyzing the result
Stream<String> words = lines.flatMap(splitToWords);
long count =
words.filter(word -> word.length() > 2)
.map(String::toLowerCase)
.distinct()
.count();
@JosePaumard#Devoxx #J8Stream
Flatmap
Another analysis of the result
Stream<String> words = lines.flatMap(splitToWords);
Map<Integer, Long> map =
words.filter(word -> word.length() > 2)
.map(String::toLowerCase)
// .distinct()
.collect(
Collectors.groupingBy(
String::length,
Collectors.counting()
)
);
@JosePaumard#Devoxx #J8Stream
Extracting the max from a Map
Yet another analysis of the result
map.entrySet().stream()
.sorted(
Entry.<Integer, Long>comparingByValue().reversed()
)
.limit(3)
.forEach(System.out::println);
@JosePaumard#Devoxx #J8Stream
Connecting a Stream on a source
Connecting a Stream on a non-standard source is possible
@JosePaumard#Devoxx #J8Stream
Connecting a Stream on a source
Connecting a Stream on a non-standard source is possible
A Stream is built on 2 things:
- A Spliterator (comes from split and iterator)
- A ReferencePipeline (the implementation)
@JosePaumard#Devoxx #J8Stream
Connecting a Stream on a source
The Spliterator is meant to be overriden
public interface Spliterator<T> {
boolean tryAdvance(Consumer<? super T> action) ;
Spliterator<T> trySplit() ;
long estimateSize();
int characteristics();
}
@JosePaumard#Devoxx #J8Stream
Connecting a Stream on a source
The Spliterator is meant to be overriden
public interface Spliterator<T> {
boolean tryAdvance(Consumer<? super T> action) ;
Spliterator<T> trySplit(); // not needed for non-parallel operations
long estimateSize(); // can return 0
int characteristics(); // returns a constant
}
@JosePaumard#Devoxx #J8Stream
Examples of custom Spliterators
Suppose we have a Stream:
[1, 2, 3, 4, 5, …]
We want to regroup the elements:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], …]
Let us build a GroupingSpliterator to do that
@JosePaumard#Devoxx #J8Stream
GroupingSpliterator
[1, 2, 3, 4, 5, …] -> [[1, 2, 3], [4, 5, 6], …]
public class GroupingSpliterator<E> implements Spliterator<Stream<E>>
{
private final long grouping ;
private final Spliterator<E> spliterator ;
// implementation
}
@JosePaumard#Devoxx #J8Stream
GroupingSpliterator
[1, 2, 3, 4, 5, …] -> [[1, 2, 3], [4, 5, 6], …]
public class GroupingSpliterator<E> implements Spliterator<Stream<E>>
{
private final long grouping ;
private final Spliterator<E> spliterator ;
// implementation
}
GroupingSpliterator<Integer> gs =
new GroupingSpliterator(spliterator, grouping);
@JosePaumard#Devoxx #J8Stream
GroupingSpliterator
Implementing estimateSize() and characteristics()
public long estimateSize() {
return spliterator.estimateSize() / grouping ;
}
public int characteristics() {
return this.spliterator.characteristics();
}
@JosePaumard#Devoxx #J8Stream
GroupingSpliterator
Implementing trySplit()
public Spliterator<Stream<E>> trySplit() {
Spliterator<E> spliterator = this.spliterator.trySplit() ;
return new GroupingSpliterator<E>(spliterator, grouping) ;
}
@JosePaumard#Devoxx #J8Stream
GroupingSpliterator
Implementing tryAdvance()
public boolean tryAdvance(Consumer<? super Stream<E>> action) {
// should call action.accept() with the next element of the Stream
// and return true if more elements are to be consumed
return true ; // false when we are done
}
@JosePaumard#Devoxx #J8Stream
GroupingSpliterator
The structure of the resulting Stream is the following:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], …]
Each element is a Stream built on the elements of the
underlying Stream
@JosePaumard#Devoxx #J8Stream
GroupingSpliterator
Build a Stream element by element is done with a
Stream.Builder
Stream.Builder<E> builder = Stream.builder() ;
for (int i = 0 ; i < grouping ; i++) {
spliterator.tryAdvance(element -> builder.add(element);
}
Stream<E> subStream = subBuilder.build(); // [1, 2, 3]
@JosePaumard#Devoxx #J8Stream
GroupingSpliterator
Are we done with the underlying Stream?
Stream.Builder<E> builder = Stream.builder() ;
for (int i = 0 ; i < grouping ; i++) {
spliterator.tryAdvance(
element -> builder.add(element)
);
}
Stream<E> subStream = subBuilder.build(); // [1, 2, 3]
@JosePaumard#Devoxx #J8Stream
GroupingSpliterator
Are we done with the underlying Stream?
Stream.Builder<E> builder = Stream.builder() ;
for (int i = 0 ; i < grouping ; i++) {
spliterator.tryAdvance( // when this call returns false
element -> builder.add(element)
);
}
Stream<E> subStream = subBuilder.build(); // [1, 2, 3]
@JosePaumard#Devoxx #J8Stream
GroupingSpliterator
Are we done with the underlying Stream?
Stream.Builder<E> builder = Stream.builder() ;
boolean finished = false;
for (int i = 0 ; i < grouping ; i++) {
if (spliterator.tryAdvance(element -> builder.add(element)))
finished = true;
}
Stream<E> subStream = subBuilder.build(); // [1, 2, 3]
@JosePaumard#Devoxx #J8Stream
GroupingSpliterator
Are we done with the underlying Stream?
public boolean tryAdvance(Consumer<? super Stream<E>> action) {
Stream.Builder<E> builder = Stream.builder() ;
boolean finished = false;
for (int i = 0 ; i < grouping ; i++) {
if (spliterator.tryAdvance(element -> builder.add(element)))
finished = true;
}
Stream<E> subStream = subBuilder.build(); // [1, 2, 3]
action.accept(subStream) ;
return !finished ;
}
@JosePaumard#Devoxx #J8Stream
RollingSpliterator
What about building a Stream like this one:
[[1, 2, 3], [2, 3, 4], [3, 4, 5], …]
This time we need a ring buffer
@JosePaumard#Devoxx #J8Stream
RollingSpliterator
The tryAdvance() call on the underlying Stream becomes this:
bufferWriteIndex is an AtomicLong
private boolean advanceSpliterator() {
return spliterator.tryAdvance(
element -> {
buffer[bufferWriteIndex.get() % buffer.length] = element ;
bufferWriteIndex.incrementAndGet() ;
});
}
@JosePaumard#Devoxx #J8Stream
RollingSpliterator
Building the element streams from the ring buffer:
private Stream<E> buildSubstream() {
Stream.Builder<E> subBuilder = Stream.builder() ;
for (int i = 0 ; i < grouping ; i++) {
subBuilder.add(
(E)buffer[(i + bufferReadIndex.get()) % buffer.length]
) ;
}
bufferReadIndex.incrementAndGet() ;
Stream<E> subStream = subBuilder.build() ;
return subStream ;
}
@JosePaumard#Devoxx #J8Stream
RollingSpliterator
Putting things together to build the tryAdvance() call
@JosePaumard#Devoxx #J8Stream
public boolean tryAdvance(Consumer<? super Stream<E>> action) {
boolean finished = false ;
if (bufferWriteIndex.get() == bufferReadIndex.get()) {
for (int i = 0 ; i < grouping ; i++) {
if (!advanceSpliterator()) {
finished = true ;
}
}
}
if (!advanceSpliterator()) {
finished = true ;
}
Stream<E> subStream = buildSubstream() ;
action.accept(subStream) ;
return !finished ;
}
@JosePaumard#Devoxx #J8Stream
Spliterator on Spliterator
We saw how to build a Stream on another Stream by
rearranging its elements
What about building a Stream by merging the elements of
other Streams?
@JosePaumard#Devoxx #J8Stream
Spliterator on Spliterators
Let us take two Streams:
[1, 2, 3, …]
[a, b, c, …]
And build a ZippingSpliterator:
[F[1, a], F[2, b], F[3, c], …]
@JosePaumard#Devoxx #J8Stream
Spliterator on Spliterators
What about
estimateSize()
trySplit()
characteristics()?
They are the same as the underlying streams
@JosePaumard#Devoxx #J8Stream
Spliterator on Spliterators
We then need to implement tryAdvance()
Where transform is a BiFunction
public boolean tryAdvance(Consumer<? super R> action) {
return spliterator1.tryAdvance(
e1 -> {
spliterator2.tryAdvance(e2 -> {
action.accept(tranform.apply(e1, e2)) ;
}) ;
}) ;
}
@JosePaumard#Devoxx #J8Stream
ZippingSpliterator
What about creating a Builder for this Spliterator?
ZippingSpliterator.Builder<String, String, String> builder =
new ZippingSpliterator.Builder();
ZippingSpliterator<String,String,String> zippingSpliterator = builder
.with(spliterator1)
.and(spliterator2)
.mergedBy((e1, e2) -> e1 + " - " + e2)
.build();
@JosePaumard#Devoxx #J8Stream
ZippingSpliterator
The complete pattern:
Stream<String> stream1 = Stream.of("one", "two", "three");
Stream<Integer> stream2 = Stream.of(1, 2, 3);
Spliterator<String> spliterator1 = stream1.spliterator();
Spliterator<Integer> spliterator2 = stream2.spliterator();
Stream<String> zipped =
StreamSupport.stream(zippingSpliterator, false);
@JosePaumard#Devoxx #J8Stream
What did we do with Streams so far?
We took one stream and built a stream by regrouping its
elements in some ways
@JosePaumard#Devoxx #J8Stream
What did we do with Streams so far?
We took one stream and built a stream by regrouping its
elements in some ways
We took to streams and we merged them, element by element,
using a bifunction
@JosePaumard#Devoxx #J8Stream
What did we do with Streams so far?
We took one stream and built a stream by regrouping its
elements in some ways
We took to streams and we merged them, element by element,
using a bifunction
What about taking one element at a time, from different
streams?
@JosePaumard#Devoxx #J8Stream
The WeavingSpliterator
We have N streams, and we want to build a stream that takes:
- its 1st element from the 1st stream
- its 2nd element from the 2nd stream, etc…
@JosePaumard#Devoxx #J8Stream
The WeavingSpliterator
The estimateSize():
public long estimateSize() {
int size = 0 ;
for (Spliterator<E> spliterator : this.spliterators) {
size += spliterator.estimateSize() ;
}
return size ;
}
@JosePaumard#Devoxx #J8Stream
The WeavingSpliterator
The tryAdvance():
private AtomicInteger whichOne = new AtomicInteger();
public boolean tryAdvance(Consumer<? super E> action) {
return spliterators[whichOne.getAndIncrement() %
spliterators.length]
.tryAdvance(action);
}
@JosePaumard#Devoxx #J8Stream
One last thing about Spliterators
The characteristics() method is in fact quite important
What does « characteristics » mean for a Spliterator?
@JosePaumard#Devoxx #J8Stream
The Spliterator
It is in fact a special word: characteristics
public interface Spliterator<T> {
public static final int ORDERED = 0x00000010;
public static final int DISTINCT = 0x00000001;
public static final int SORTED = 0x00000004;
public static final int SIZED = 0x00000040;
public static final int NONNULL = 0x00000100;
public static final int IMMUTABLE = 0x00000400;
public static final int CONCURRENT = 0x00001000;
public static final int SUBSIZED = 0x00004000;
}
@JosePaumard#Devoxx #J8Stream
The Spliterator
The Spliterator holds a special word: characteristics
// ArrayListSpliterator
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED |
Spliterator.SUBSIZED;
}
// HashMap.KeySpliterator
public int characteristics() {
return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
Spliterator.DISTINCT;
}
@JosePaumard#Devoxx #J8Stream
The Spliterator
The Spliterator holds a special word: characteristics
This word is used for optimization
people.stream()
.sorted() // quicksort?
.collect(Collectors.toList());
@JosePaumard#Devoxx #J8Stream
The Spliterator
The Spliterator holds a special word: characteristics
This word is used for optimization
people.stream()
.sorted() // quicksort? It depends on SORTED == 0
.collect(Collectors.toList());
@JosePaumard#Devoxx #J8Stream
The Spliterator
The Spliterator holds a special word: characteristics
This word is used for optimization
SortedSet<Person> people = ...;
people.stream()
.sorted() // SORTED == 1, no quicksort
.collect(Collectors.toList());
@JosePaumard#Devoxx #J8Stream
The Spliterator
The Spliterator holds a special word: characteristics
This word is used for optimization
ArrayList<Person> people = ...;
people.stream()
.sorted() // SORTED == 0, quicksort
.collect(Collectors.toList());
@JosePaumard#Devoxx #J8Stream
The characteristics can change
Each Stream object in a pipeline has its own characteristics
@JosePaumard#Devoxx #J8Stream
The characteristics can change
Each Stream object in a pipeline has its own characteristics
Method Set to 0 Set to 1
filter() SIZED -
map() DISTINCT, SORTED -
flatMap() DISTINCT, SORTED, SIZED -
sorted() - SORTED, ORDERED
distinct() - DISTINCT
limit() SIZED -
peek() - -
unordered() ORDERED -
@JosePaumard#Devoxx #J8Stream
The characteristics can change
Each Stream object in a pipeline has its own characteristics
Method Set to 0 Set to 1
filter() SIZED -
map() DISTINCT, SORTED -
flatMap() DISTINCT, SORTED, SIZED -
sorted() - SORTED, ORDERED
distinct() - DISTINCT
limit() SIZED -
peek() - -
unordered() ORDERED -
@JosePaumard#Devoxx #J8Stream
The characteristics can change
Each Stream object in a pipeline has its own characteristics
Method Set to 0 Set to 1
filter() SIZED -
map() DISTINCT, SORTED -
flatMap() DISTINCT, SORTED, SIZED -
sorted() - SORTED, ORDERED
distinct() - DISTINCT
limit() SIZED -
peek() - -
unordered() ORDERED -
@JosePaumard#Devoxx #J8Stream
Back to the Spliterator itself
What did we do with the spliterator, and with the Stream built
on it?
@JosePaumard#Devoxx #J8Stream
What did we do with Streams so far?
We took one stream and built a stream by regrouping its
elements in some ways
We took to streams and we merged them, element by element,
using a bifunction
We took a collection of streams and built a stream by taking
elements from them, in a given order
@JosePaumard#Devoxx #J8Stream
Wrap-up for the Stream API
The Java 8 Stream API is not just about implementing map /
filter / reduce or building hashmaps
We can use it to manipulate data in advanced ways:
- by deciding on what source we want to connect
- by deciding how we can consume the data from that source
Reactive Streams
RxJava
@JosePaumard#Devoxx #J8Stream
RxJava
Open source API, available on Github
Developed by Netflix
RxJava is the Java version of ReactiveX
.NET
Python, Kotlin, JavaScript, Scala, Ruby, Groovy, Rust
Android
https://github.com/ReactiveX/RxJava
@JosePaumard#Devoxx #J8Stream
RxJava
RxJava is not an alternative implementation of Java 8
Streams, nor the Collection framework
It is an implementation of the Reactor pattern
@JosePaumard#Devoxx #J8Stream
RxJava
The central class is the Observable class
@JosePaumard#Devoxx #J8Stream
RxJava
The central class is the Observable class
It’s big: ~10k lines of code
@JosePaumard#Devoxx #J8Stream
RxJava
The central class is the Observable class
It’s big: ~10k lines of code
It’s complex: ~100 static methods, ~150 non-static methods
@JosePaumard#Devoxx #J8Stream
RxJava
The central class is the Observable class
It’s big: ~10k lines of code
It’s complex: ~100 static methods, ~150 non-static methods
It’s complex: not only because there is a lot of things in it, but
also because the concepts are complex
@JosePaumard#Devoxx #J8Stream
RxJava
Interface Observer
used to « observe » an observable
@JosePaumard#Devoxx #J8Stream
RxJava
Interface Observer
used to « observe » an observable
Interface Subscription
used to model the link between an observer and an
observable
@JosePaumard#Devoxx #J8Stream
Interface Observer
A simple interface
public interface Observer<T> {
public void onNext(T t);
public void onCompleted();
public void onError(Throwable e);
}
@JosePaumard#Devoxx #J8Stream
How to subscribe
Subscribing to an observable
Observable<T> observable = ... ;
Subscription subscription = observable.subscribe(observer) ;
@JosePaumard#Devoxx #J8Stream
How to subscribe
Subscribing to an observable
Observable<T> observable = ... ;
Subscription subscription = observable.subscribe(observer) ;
public interface Subscription {
public void unsubscribe();
public void isUnsubscribe();
}
@JosePaumard#Devoxx #J8Stream
How to subscribe
We can also just declare a callback
Observable<T> observable = ... ;
Observable<T> next = observable.doOnNext(System.out::println) ;
Observable<T> error = observable.doOnError(System.out::println) ;
Observable<T> each = observable.doOnEach(System.out::println) ;
@JosePaumard#Devoxx #J8Stream
RxJava – agenda
Patterns to create Observables
How to merge observables together
Hot observables / backpressure
@JosePaumard#Devoxx #J8Stream
How to create
an Observable
@JosePaumard#Devoxx #J8Stream
The usual ways
Observable from collections and arrays
Observable<String> obs1 = Observable.just("one", "two", "three") ;
List<String> strings = Arrays.asList("one", "two", "three") ;
Observable<String> obs2 = Observable.from(strings) ;
@JosePaumard#Devoxx #J8Stream
The usual ways
Observable useful for tests
never(): never emits anything
Observable<String> empty = Observable.empty() ;
Observable<String> never = Observable.never() ;
Observable<String> error = Observable.<String>error(exception) ;
@JosePaumard#Devoxx #J8Stream
The usual ways
Series and time series
Observable<Long> longs = Observable.range(1L, 100L) ;
// interval
Observable<Long> timeSerie1 =
Observable.interval(1L, TimeUnit.MILLISECONDS) ; // serie of longs
// initial delay, then interval
Observable<Long> timeSerie2 =
Observable.timer(10L, 1L, TimeUnit.MILLISECONDS) ; // one 0
@JosePaumard#Devoxx #J8Stream
The usual ways
The using() method
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
) { }
@JosePaumard#Devoxx #J8Stream
How using() works
The using() method
1) Creates a resource that will provide the elements
2) Uses this resource to create the elements
3) Applies the 3rd argument to the resource
This 3rd element is in fact there to close the resource
@JosePaumard#Devoxx #J8Stream
How using() works
Suppose we want to create an observable on the lines of a text
file
1) We need a BufferedReader
2) The we need to wrap the lines one by one in Observables
3) Then we need to close the BufferedReader
@JosePaumard#Devoxx #J8Stream
How using() works
Let us do that:
Func0<BufferedReader> resourceFactory =
() -> new BufferedReader(new FileReader(new File(fileName)));
Action1<BufferedReader> disposeAction =
br -> br.close();
@JosePaumard#Devoxx #J8Stream
How using() works
The problem is that… we need to handle exceptions 
Func0<BufferedReader> resourceFactory =
() -> {
try {
return new BufferedReader(
new FileReader(new File(fileName)));
} catch (Exception e) {
e.printStackTrace();
}
return null;
};
@JosePaumard#Devoxx #J8Stream
How using() works
How can we create one Observable by file line?
@JosePaumard#Devoxx #J8Stream
How using() works
How can we create one Observable by file line?
We could read the file line by line, put the lines in a Collection,
then build an Observable on that collection
@JosePaumard#Devoxx #J8Stream
How using() works
How can we create one Observable by file line?
We could read the file line by line, put the lines in a Collection,
then build an Observable on that collection
@JosePaumard#Devoxx #J8Stream
How using() works
How can we create one Observable by file line?
We could read the file line by line, put the lines in a Iterator,
then build an Observable on that iterator
@JosePaumard#Devoxx #J8Stream
How using() works
How can we create one Observable by file line?
We could read the file line by line, put the lines in a Iterator,
then build an Observable on that iterator
Creating an Observable from an Iterator:
Observable.from(() -> iterator);
@JosePaumard#Devoxx #J8Stream
Func1<BufferedReader, String> observableFactory = bufferedReader -> {
Iterator<String> readerIterator = new Iterator<String>() {
private String currentLine = null;
private boolean finished;
{
currentLine = bufferedReader.readLine();
finished = currentLine == null;
}
public boolean hasNext() {
return !finished;
}
public String next() {
String line = currentLine;
currentLine = bufferedReader.readLine();
finished = currentLine == null;
return line;
}
};
return Observable.from(() -> readerIterator);
}
@JosePaumard#Devoxx #J8Stream
How using() works
The using() method
Made to build an Observable on a resource that needs to be
« closed » at the end of the generation of the stream
@JosePaumard#Devoxx #J8Stream
How using() works
The using() method
Made to build an Observable on a resource that needs to be
« closed » at the end of the generation of the stream
It is not built on the auto closeable mechanism because
RxJava aims to be Java 6 compatible
@JosePaumard#Devoxx #J8Stream
Schedulers
@JosePaumard#Devoxx #J8Stream
RxJava & executors
Some of those methods take a further argument
This Observable is executed in this scheduler
Scheduler is an interface (in fact an abstract class, to define
« default methods » in Java 7)
Schedulers should be used to create schedulers
Observable<Long> longs = Observable.range(0L, 100L, scheduler) ;
@JosePaumard#Devoxx #J8Stream
RxJava & executors
It allows for the execution of Observable in specialized pools of
Threads
Some Observables are executed in special schedulers (cf
Javadoc)
@JosePaumard#Devoxx #J8Stream
Schedulers
Factory Schedulers
public final class Schedulers {
public static Scheduler immediate() {...} // immediate
public static Scheduler newThread() {...} // new thread
public static Scheduler trampoline() {...} // queued in the current
// thread
}
@JosePaumard#Devoxx #J8Stream
Schedulers
Factory Schedulers
public final class Schedulers {
public static Scheduler computation() {...} // computation ES
public static Scheduler io() {...} // IO growing ES
public static Scheduler test() {...}
public static Scheduler from(Executor executor) {...}
}
@JosePaumard#Devoxx #J8Stream
Schedulers
Schedulers can be seen as Executors
Some of them are specialized (IO, Computation)
Observers are called in the thread that runs the Observable
@JosePaumard#Devoxx #J8Stream
Schedulers
The Schedulers.from(executor) is useful to call observers in
certain threads
For instance:
Scheduler swingScheduler =
Schedulers.from(
SwingUtilities::invokeLater
);
@JosePaumard#Devoxx #J8Stream
A 1st example
A simple example
Observable<Integer> range1To100 = Observable.range(1L, 100L) ;
range1To100.subscribe(System.out::println) ;
@JosePaumard#Devoxx #J8Stream
A 1st example
A simple example
Observable<Integer> range1To100 = Observable.range(1L, 100L) ;
range1To100.subscribe(System.out::println) ;
> 1 2 3 4 ... 100
@JosePaumard#Devoxx #J8Stream
A 2nd example
A not so simple example
Observable<Integer> timer = Observable.timer(1, TimeUnit.SECONDS) ;
timer.subscribe(System.out::println) ;
@JosePaumard#Devoxx #J8Stream
A 2nd example
A not so simple example
Nothing is printed
Observable<Integer> timer = Observable.timer(1, TimeUnit.SECONDS) ;
timer.subscribe(System.out::println) ;
>
@JosePaumard#Devoxx #J8Stream
A 2nd example
Let us modify this code
Observable<Integer> timer = Observable.timer(1, TimeUnit.SECONDS) ;
timer.subscribe(() -> {
System.out.println(Thread.currentThread().getName() + " " +
Thread.currentThread().isDaemon()) ;
}) ;
Thread.sleep(2) ;
@JosePaumard#Devoxx #J8Stream
A 2nd example
Let us modify this code
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
@JosePaumard#Devoxx #J8Stream
About the 1st & 2nd examples
The first example ran in the main thread
@JosePaumard#Devoxx #J8Stream
About the 1st & 2nd examples
The first example ran in the main thread
The second example ran in a daemon thread, that does not
prevent the JVM from exiting. Nothing was printed out,
because it did not have the time to be executed.
@JosePaumard#Devoxx #J8Stream
About the 1st & 2nd examples
The first example ran in the main thread
The second example ran in a daemon thread, that does not
prevent the JVM from exiting. Nothing was printed out,
because it did not have the time to be executed.
Observable are ran in their own schedulers (executors)
@JosePaumard#Devoxx #J8Stream
Merging Observables
@JosePaumard#Devoxx #J8Stream
Merging Observable together
RxJava provides a collection of methods to merge observables
together
@JosePaumard#Devoxx #J8Stream
Merging Observable together
RxJava provides a collection of methods to merge observables
together
With many different, very interesting semantics
@JosePaumard#Devoxx #J8Stream
Representing an Observable
RxJava uses « marble diagrams » to represent observables
Observable
@JosePaumard#Devoxx #J8Stream
Representing an Observable
RxJava uses « marble diagrams » to represent observables
On this example, onNext() is called 5 times
Observable
@JosePaumard#Devoxx #J8Stream
Representing an Observable
RxJava uses « marble diagrams » to represent observables
Here an exception has been thrown, no data will be generated
after it, the onError() method is called
Observable
@JosePaumard#Devoxx #J8Stream
Representing an Observable
RxJava uses « marble diagrams » to represent observables
Here the end of the Stream has been reached, the
onComplete() method is called
Observable
@JosePaumard#Devoxx #J8Stream
The ambiguous operator
The first operator is amb(), that stands for « ambiguous »
It takes the first Observable that produced data
©RxJava
O1
O2
result
@JosePaumard#Devoxx #J8Stream
The zip operator
The zip operator takes one element from each Observable and
combine them using a function
©RxJava
O1
O2
F(O1, O2)
@JosePaumard#Devoxx #J8Stream
The combine latest operator
The combineLatest is a non-strict zip operator, emits an item
each time an observable emits one
©RxJava
O1
O2
F(O1, O2)
@JosePaumard#Devoxx #J8Stream
Concat and merge
Concat: emits O1 and then O2, without mixing them,
merge stops on the emission of an error
©RxJava©RxJava
@JosePaumard#Devoxx #J8Stream
Observables of Observables
The methods we saw are defined on Iterables of Observables
public final static <T> Observable<T> merge(
Iterable<Observable<T>> listOfSequences) { }
@JosePaumard#Devoxx #J8Stream
Observables of Observables
The methods we saw are defined on Iterables of Observables
They are also defined on Observables of Observables
public final static <T> Observable<T> merge(
Iterable<Observable<T>> listOfSequences) { }
public final static <T> Observable<T> merge(
Observable<Observable<T>> sequenceOfSequences) { }
@JosePaumard#Devoxx #J8Stream
Observables of Observables
And the marble diagrams are different
public final static <T> Observable<T> merge(
Iterable<Observable<T>> listOfSequences) { }
©RxJava
@JosePaumard#Devoxx #J8Stream
Observables of Observables
And the marble diagrams are different
public final static <T> Observable<T> merge(
Observable<Observable<T>> sequenceOfSequences) { }
©RxJava
@JosePaumard#Devoxx #J8Stream
Observables of Observables
Then comes the switchOnNext operator
public final static <T> Observable<T> switchOnNext(
Observable<Observable<T>> sequenceOfSequences) { }
©RxJava
@JosePaumard#Devoxx #J8Stream
Hot and cold
Observables
@JosePaumard#Devoxx #J8Stream
Cold and hot Observable
A cold Observable emits items if it is observered
@JosePaumard#Devoxx #J8Stream
Cold and hot Observable
A cold Observable emits items if it is observered
A hot Observable emits items when it is created
@JosePaumard#Devoxx #J8Stream
A 3rd example
Let us see an example
Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS);
Observable<String> manyStrings =
Observable.combineLatest(
timer, Observable.just("one"),
(i, s) -> i + " - " + s).forEach(System.out::println);
@JosePaumard#Devoxx #J8Stream
A 3rd example
Let us see an example
Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS);
Observable<String> manyStrings =
Observable.combineLatest(
timer, Observable.just("one"),
(i, s) -> i + " - " + s).forEach(System.out::println);
> 0 - one
1 - one
2 – one ...
@JosePaumard#Devoxx #J8Stream
A 3rd example
Let us see an example
Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS);
Thread.sleep(5500);
Observable<String> manyStrings =
Observable.combineLatest(
timer, Observable.just("one"),
(i, s) -> i + " - " + s).forEach(System.out::println);
@JosePaumard#Devoxx #J8Stream
A 3rd example
Let us see an example
Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS);
Thread.sleep(5500);
Observable<String> manyStrings =
Observable.combineLatest(
timer, Observable.just("one"),
(i, s) -> i + " - " + s).forEach(System.out::println);
> 0 - one
1 - one
2 – one ...
@JosePaumard#Devoxx #J8Stream
A 3rd example
It means that the timer does not increment itself if not
observed
This Observable is cold
@JosePaumard#Devoxx #J8Stream
A 3rd example
It means that the timer does not increment itself if not
observed
This Observable is cold
How can we make it hot?
@JosePaumard#Devoxx #J8Stream
A 3rd example
Making it hot:
Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS);
Observable<String> manyStrings =
Observable.combineLatest(
timer, Observable.just("one"),
(i, s) -> i + " - " + s).forEach(System.out::println);
Observable<String> moreManyStrings =
Observable.combineLatest(
timer, Observable.just("two"),
(i, s) -> i + " - " + s).forEach(System.out::println);
@JosePaumard#Devoxx #J8Stream
A 3rd example
It prints out the following:
> 0 - one
0 - two
1 – one
1 - two
2 – one
2 – two ...
@JosePaumard#Devoxx #J8Stream
A 3rd example
Making it hot:Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS);
Observable<String> manyStrings =
Observable.combineLatest(
timer, Observable.just("one"),
(i, s) -> i + " - " + s).forEach(System.out::println);
Thread.sleep(5500);
Observable<String> moreManyStrings =
Observable.combineLatest(
timer, Observable.just("two"),
(i, s) -> i + " - " + s).forEach(System.out::println);
@JosePaumard#Devoxx #J8Stream
A 3rd example
It then prints out the following!
> 0 - one
1 - one
2 - one
3 - one
4 - one
5 - one
5 - two
6 - one
6 - two
7 - one
7 – two ...
@JosePaumard#Devoxx #J8Stream
Cold and hot Observable
So in its first use, timer is a cold Observable
And in the second one, it becomes a hot Observable
If used more than once, a cold Observable will be seen hot by
the 2nd and later observers
@JosePaumard#Devoxx #J8Stream
Cold and hot Observable
Problem: what happens if we have many observers to
subscribe?
@JosePaumard#Devoxx #J8Stream
Cold and hot Observable
Problem: what happens if we have many observers to
subscribe?
Our application could miss the first values emitted by an
observable
How can we fix that?
@JosePaumard#Devoxx #J8Stream
Cold and hot Observable
We can use the cache method
cache(int capacity) { }
O1
O2
@JosePaumard#Devoxx #J8Stream
A cached timer
Creating a cached timer:
Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS);
Observable<Long> cachedTimer = timer.cache(1_000);
...
@JosePaumard#Devoxx #J8Stream
ConnectableObservable
We can do better and use a ConnectableObservable
(which is an Observable)
// does not count as a subscription
ConnectableObservable<Long> publish = observable.publish();
// take the time to connect all the observers
// then call
publish.connect();
@JosePaumard#Devoxx #J8Stream
Cold and hot Observable
In fact the Observable returned by the connect() call is itself a
hot Observable
So this is also a way to create a hot Observable
Coffe break?
Coffe break!Map, filter, reduce, collect
Repeating & retrying
Dealing with time
Backpressure
Combining Java 8 Streams
& RxJava
Comparisons
The future: reactive
support in Java 9
@JosePaumard#Devoxx #J8Stream
Map, filter,
reduce and collect
@JosePaumard#Devoxx #J8Stream
Map and filter
Map / filter, return an Observable
Emits only the elements that match the predicate
map(Func1<T, R> mapping) { }
cast(Class<R> clazz) { } // casts the elements
filter(Func1<T, Boolean> predicate) { }
@JosePaumard#Devoxx #J8Stream
Materializing
Materialize: special type of mapping
Emit a Notification object that wraps the item, with metadata
Useful for logging
Does the reverse:
materialize() { } // Observable<Notification<T>>
dematerialize() { } // Observable<T>
@JosePaumard#Devoxx #J8Stream
Selecting ranges
Selecting ranges of elements
first() { }
last() { }
skip(int n) { }
limit(int n) { }
take(int n) { }
@JosePaumard#Devoxx #J8Stream
Special filtering
Special functions
Emits a true then complete on the onComplete
if the Observable emitted at least one item
exists(Func1<T, Boolean> predicate) { }
@JosePaumard#Devoxx #J8Stream
Special filtering
Special functions
Emits the nth item then complete on the onNext
if the Observable emitted at least n items
elementAt(int n) { }
@JosePaumard#Devoxx #J8Stream
Special filtering
Special functions
Emits nothing then complete on the onComplete
ignoreElement() { }
@JosePaumard#Devoxx #J8Stream
Special filtering
Special functions
Emits the first item on the onComplete
or emits an error on the second item emitted
single() { }
@JosePaumard#Devoxx #J8Stream
Special filtering
Special functions
Emits the matching item if it has been seen, on onComplete
or emits an error if not, on onComplete
single(Func1<T, Boolean> predicate) { }
@JosePaumard#Devoxx #J8Stream
Reductions & collections
Classical reductions
all(Func1<T, Boolean> predicate) { } // Observable<Boolean>
count() { } // Observable<Long>
forEach(Action1<T> consumer) { } // void
@JosePaumard#Devoxx #J8Stream
Reductions & collections
Accumulations
Reduce: returns the reduction of all the items, on onComplete
Returns an error if the observable is empty
reduce(Func2<T, T, T> accumulator) { } // Observable<T>
@JosePaumard#Devoxx #J8Stream
Reductions & collections
Accumulations
Reduce: returns the reduction of all the items, on onComplete
In case the observable might be empty, use the other version
of reduce, that takes a seed
reduce(Func2<T, T, T> accumulator) { } // Observable<T>
reduce(seed, Func2<T, T, T> accumulator) { } // Observable<T>
@JosePaumard#Devoxx #J8Stream
Reductions & collections
Accumulations
Scan: returns the reduction step by step, on onNext
Can also take a seed
reduce(Func2<T, T, T> accumulator) { } // Observable<T>
scan(Func2<T, T, T> accumulator) { } // Observable<T>
reduce(seed, Func2<T, T, T> accumulator) { } // Observable<T>
@JosePaumard#Devoxx #J8Stream
Reductions & collections
Collecting data in a mutable container
Example: adding the elements to a list
collect(Func0<R> stateFactory, // Producer
Action2<R, T> collector) { } // BiConsumer
collect(ArrayList::new, // Producer
ArrayList::add) { } // BiConsumer
@JosePaumard#Devoxx #J8Stream
Reductions & collections
Ready to use collectors for lists:
toList() { } // Observable<List<T>>
toSortedList() { } // Observable<List<T>>
@JosePaumard#Devoxx #J8Stream
Reductions & collections
Ready to use collectors for lists:
And for maps (toMap can lose items):
toList() { } // Observable<List<T>>
toSortedList() { } // Observable<List<T>>
toMultimap(Func1<T, K> keySelector, // Observable<
Func1<T, V> valueSelector) { } // Map<K, Collection<T>>
toMap(Func1<T, K> keySelector) { } // Observable<Map<K, T>>
@JosePaumard#Devoxx #J8Stream
Reductions & collections
A special kind of map:
The returned observable could hold a single map,
but it does not
groupBy(Func1<T, K> keySelector) { } // function
@JosePaumard#Devoxx #J8Stream
Reductions & collections
A special kind of map:
The returned observable could hold a single map,
but it does not
It holds GroupedObservable items, which is an Observable
with a key
groupBy(Func1<T, K> keySelector) { } // function
@JosePaumard#Devoxx #J8Stream
Reductions & collections
A special kind of map:
The returned observable could hold a single map,
but it does not
Instead of returning an Iterable of key / value pairs
It returns an Observable of key / value pairs
groupBy(Func1<T, K> keySelector) { } // function
@JosePaumard#Devoxx #J8Stream
Reductions & collections
Let us see an example
Observable<Person> people = ...;
// Observable<GroupedObservable<Integer, Person>>
people.groupBy(Person::getAge)
@JosePaumard#Devoxx #J8Stream
Reductions & collections
Let us see an example
Observable<Person> people = ...;
// Observable<GroupedObservable<Integer, Person>>
people.groupBy(Person::getAge)
.filter(groupedObs -> groupedObs.getKey() > 20)
@JosePaumard#Devoxx #J8Stream
Reductions & collections
Let us see an example
Observable<Person> people = ...;
// GroupedObservable<Integer, Person> extends Observable<Person>
people.groupBy(Person::getAge)
.filter(groupedObs -> groupedObs.getKey() > 20)
.flatMap(groupedObs -> groupedObs)
@JosePaumard#Devoxx #J8Stream
Reductions & collections
Let us see an example
Observable<Person> people = ...;
// Observable<Integer>
people.groupBy(Person::getAge)
.filter(groupedObs -> groupedObs.getKey() > 20)
.flatMap(groupedObs -> groupedObs)
.count();
@JosePaumard#Devoxx #J8Stream
Wrap-up on map / filter / reduce
All those methods return an Observable
The result is thus wrapped in an Observable
Chaining operations is made with flatMap()
Wrapping and unwrapping has a cost…
@JosePaumard#Devoxx #J8Stream
Repeating and
Retrying
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
The repeat method repeats the same Observable
repeat() { } // Observable<T>
repeat(long times) { } // Observable<T>
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
The repeat method repeats the same Observable
repeat() { } // Observable<T>
repeat(long times) { } // Observable<T>
repeatWhen(
Func1<Observable<Void>>, Observable<?>> notificationHandler
) { }
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
repeat: will repeat the same Observable again and again
repeatWhen:
- on the onComplete of the source Observable, the
notification handler is passed an Observable
- if the notification handler calls the onError or onComplete of
this passed Observable, then repeatWhen calls the same on
the returned Observable
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
repeat: will repeat the same Observable again and again
On the returned Observable, one can invoke:
onNext(), that triggers the repetition
onComplete() or onError(), which will trigger the same call on
the source Observable
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
This function takes an Observable<Void>
And returns an Observable<?>
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
This function takes an Observable<Void>
And returns an Observable<?>
When the Observable to be repeated calls onComplete
Then the passed Observable calls onNext with null
As a response the returned Observable should call:
- onNext to repeat
- onComplete to stop repeating
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
The first thing we need is an Observable on which we can emit
items on demand
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
The first thing we need is an Observable on which we can emit
items on demand
For that, we need to implement the OnSubscribe interface
final Set<Subscriber<? super Void>> subscribers = new HashSet<>();
OnSubscribe<Void> onSubscribe = new OnSubscribe<Void>() {
public void call(Subscriber<? super Void> subscriber) {
subscribers.add(subscriber);
}
};
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
Then create an Observable from this onSubscribe object
final Observable<Void> returnedObservable =
Observable.create(onSubscribe);
final Set<Subscriber<? super Void>> subscribers = new HashSet<>();
OnSubscribe<Void> onSubscribe = new OnSubscribe<Void>() {
public void call(Subscriber<? super Void> subscriber) {
subscribers.add(subscriber);
}
};
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
Then create the notification handler
Func1 notificationHandler =
new Func1<Observable<Void>, Observable<Void>>() {
final Observable<Void> returnedObservable = ...;
public Observable<Void> call(Observable<Void> observable) {
// implementation
}
return returnedObservable;
};
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
Then create the notification handler
observable.subscribe(new Observer<Void>() {
private int count = 0;
public void onCompleted() {
}
public void onError(Throwable e) {
subscribers.forEach(sub -> sub.onError(e));
}
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
Then create the notification handler
int count = 0;
public void onNext(Void t) {
if (count == 3) {
subscribers.forEach(sub -> sub.onCompleted()); // stops
} else {
count++;
Thread.sleep(1_000);
subscribers.forEach(sub -> sub.onNext(null)); // repeat
}
}
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
The complete pattern
Observable<Long> timer =
Observable.interval(1, TimeUnit.MILLISECONDS).take(5);
timer.repeatWhen(
(Func1<? super Observable<? extends Void>, ? extends Observable<?>>)
notificationHandler)
.forEach(System.out::println);
Thread.sleep(5_000);
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
The retry method will reset the Observable on error
The retryThen() function works in the same way as for the
repeat
retry() { } // Observable<T>
retry(long times) { } // Observable<T>
retryWhen(
Func1<Observable<Throwable>>, Observable<?>> notificationHandler
) { }
@JosePaumard#Devoxx #J8Stream
Repeating & retrying
There is also a retry family of methods, that works on onError
instead of onComplete
@JosePaumard#Devoxx #J8Stream
Joining
Joins to Observable, based on the overlapping of durations
join(Observable<TRight> right,
Func1<T, Observable<TLeftDuration>> leftDurationSelector,
Func1<TRight, Observable<TRightDuration>> rightDurationSelector,
Func2<T, TRight, R> resultSelector) { }
@JosePaumard#Devoxx #J8Stream
Join
Joins to Observable, based on the overlapping of durations
©RxJava
Left obs.
Right obs.
F(O1, O2)
@JosePaumard#Devoxx #J8Stream
GroupJoin
Joins to Observable, based on the overlapping of durations
groupJoin(
Observable<T2> right,
Func1<T, Observable<D1>> leftDuration,
Func1<T2, Observable<D2>> rightDuration,
Func2<T, <T2>, R> resultSelector)
{ }
@JosePaumard#Devoxx #J8Stream
GroupJoin
Joins to Observable, based on the overlapping of durations
©RxJava
Left obs.
Right obs.
F(O1, O2)
@JosePaumard#Devoxx #J8Stream
Dealing with time
@JosePaumard#Devoxx #J8Stream
Dealing with the time
RxJava has a set of methods to deal with time
Let us go back on our cold / hot Observables
It is easy to imagine a hot Observable that emits an onNext
event each second, thus playing the role of a clock
@JosePaumard#Devoxx #J8Stream
Sampling
With hot observables, RxJava can synchronize on a clock
sample(long period, TimeUnit timeUnit) { }
©RxJava
@JosePaumard#Devoxx #J8Stream
Sampling
Or synchronize on a reference Observable!
sample(Observable<U> sampler) { } // samples on emission & completion
©RxJava
O1
sampler
@JosePaumard#Devoxx #J8Stream
RxJava and synchronization
A very powerful feature:
RxJava can synchronize on a clock (real-time)
And on another Observable, it can then take its own time scale
@JosePaumard#Devoxx #J8Stream
Measuring time
If we can measure time, then we can emit it
Will emit an the amount of time between the two last onNext
events
timeInterval() { } // Observable<TimeInterval<T>>
@JosePaumard#Devoxx #J8Stream
Measuring time
If we can measure time, then we can delay an emission
Will reemit the items, with a delay
This delay can be computed from the items themselves
delay(long delay, TimeUnit timeUnit) ; // Observable<T>
delay(Func1<T, Observable<U> func1) ; // Observable<T>
@JosePaumard#Devoxx #J8Stream
Measuring time
If we can measure time, then we can timeout
Will emit an error if no item is seen during this time
timeout(long n, TimeUnit timeUnit) { }
@JosePaumard#Devoxx #J8Stream
Backpressure
@JosePaumard#Devoxx #J8Stream
Fast hot observables
What happens if a hot Observable emits too many items?
What happens when an Observer cannot keep up the pace of
an Observable?
@JosePaumard#Devoxx #J8Stream
Fast hot observables
What happens if a hot Observable emits too many items?
What happens when an Observer cannot keep up the pace of
an Observable?
This leads to the notion of backpressure
@JosePaumard#Devoxx #J8Stream
Backpressure methods
Backpressure is a way to slow down the emission of elements
It can act on the observing side
Several strategies:
- Buffering items
- Skipping items (sampling is a way to implement this)
@JosePaumard#Devoxx #J8Stream
Backpressure methods
Use cases can be very different!
1) The temperature sensors example
- Suppose our application handle a group of temperature
sensors
- Each sensor sends one measure every seconds
@JosePaumard#Devoxx #J8Stream
Backpressure methods
Use cases can be very different!
1) The temperature sensors example
- Suppose our application handle a group of temperature
sensors
- Each sensor sends one measure every seconds
Maybe we can just keep one measure every hour!
Loosing items ≠ loosing information!
@JosePaumard#Devoxx #J8Stream
Backpressure methods
Use cases can be very different!
2) The YouTube example
- YouTube sends a HD video to your browser
- But my bandwidth is too low
@JosePaumard#Devoxx #J8Stream
Backpressure methods
Use cases can be very different!
2) The YouTube example
- YouTube sends a HD video to your browser
- But my bandwidth is too low
Instead of sending HD, YouTube will send me 720p
@JosePaumard#Devoxx #J8Stream
Backpressure methods
Use cases can be very different!
2) The YouTube example
- YouTube sends a HD video to your browser
- But my bandwidth is too low
Instead of sending HD, YouTube will send me 720p
Loosing items ≠ loosing information!
@JosePaumard#Devoxx #J8Stream
Backpressure
Here we are looking at the backpressure from the observer
point of view
But it can also clog a network
Acting at the Observable level can be important too!
@JosePaumard#Devoxx #J8Stream
Buffering
Buffering records data in a buffer and emits it
buffer(int size) { } // Observable<List<T>>
buffer(long timeSpan, TimeUnit unit) { } // Observable<List<T>>
buffer(long timeSpan, TimeUnit unit, int maxSize) { }
@JosePaumard#Devoxx #J8Stream
Buffering
Buffering records data in a buffer (a list) and emits it
buffer(int size) { } // Observable<List<T>>
buffer(long timeSpan, TimeUnit unit) { } // Observable<List<T>>
buffer(long timeSpan, TimeUnit unit, int maxSize) { }
buffer(Observable<O> bufferOpenings, // Openings events
Func1<O, Observable<C>> bufferClosings) { } // Closings events
@JosePaumard#Devoxx #J8Stream
Windowing
Windowing acts as a buffer, but emits Observables instead of
lists of buffered items
window(int size) { } // Observable<Observable<T>>
window(long timeSpan, TimeUnit unit) { } // Observable<Observable<T>>
window(long timeSpan, TimeUnit unit, int maxSize) { }
window(Observable<O> bufferOpenings, // Openings events
Func1<O, Observable<C>> bufferClosings) { } // Closings events
@JosePaumard#Devoxx #J8Stream
Buffering & windowing
©RxJava
©RxJava
@JosePaumard#Devoxx #J8Stream
Throttling
Throttling is a sampler on the beginning or the end of a window
throttleFirst(long windowDuration, TimeUnit unit) { } // Observable<T>
throttleLast(long windowDuration, TimeUnit unit) { }
throttleWithTimeout(long windowDuration, TimeUnit unit) { }
@JosePaumard#Devoxx #J8Stream
Throttling
throttleFirst: emits the first item in a window of time
throttleLast: emits the last item from that window, on the next
tick of the clock
@JosePaumard#Devoxx #J8Stream
Throttling
Throttling is a sampler on the beginning or the end of a window
©RxJava
@JosePaumard#Devoxx #J8Stream
Debouncing
Debounce also limits the rate of the emission of the items, by
adding a delay before reemitting
debounce(long delay, TimeUnit timeUnit) ; // Observable<T>
debounce(Func1<T, Observable<U> func1) ; // Observable<T>
@JosePaumard#Devoxx #J8Stream
Debouncing
It two items are emitted within the same time frame, the first
one is lost
©RxJava
@JosePaumard#Devoxx #J8Stream
Wrap-up on RxJava
Complex API, many different concepts, many methods
Allows to process data in chosen threads, this is useful for IO,
computations, specialized threads (GUI threads)
Allows the synchronization of operations
on clocks
on application events
Works in pull mode, and also in push mode
backpressure
Getting the best of
both worlds?
@JosePaumard#Devoxx #J8Stream
Getting the best of both API?
What about connecting a J8 Stream to a Rx Observable?
@JosePaumard#Devoxx #J8Stream
Getting the best of both API?
If we have an Iterator, this is easy:
Iterator<T> iterator = ... ;
Observable<T> observable = Observable.from(() -> iterator) ;
@JosePaumard#Devoxx #J8Stream
Getting the best of both API?
If we have an Iterator, this is easy:
If we have a Spliterator, not much more complex:
Iterator<T> iterator = ... ;
Observable<T> observable = Observable.from(() -> iterator) ;
Spliterator<T> spliterator = ... ;
Observable<T> observable =
Observable.from(() -> Spliterators.iterator(spliterator)) ;
@JosePaumard#Devoxx #J8Stream
Getting the best of both API?
So if we have a Stream, we can easily build an Observable
@JosePaumard#Devoxx #J8Stream
Getting the best of both API?
So if we have a Stream, we can easily build an Observable
What about the other way?
@JosePaumard#Devoxx #J8Stream
Getting the best of both API?
So if we have a Stream, we can easily build an Observable
What about the other way?
1) We can build an Iterator on an Observable
2) Then build a Spliterator on an Iterator
@JosePaumard#Devoxx #J8Stream
Getting the best of both API?
So if we have a Stream, we can easily build an Observable
What about the other way?
1) We can build an Iterator on an Observable
2) Then build a Spliterator on an Iterator
But we need to do that ourselves…
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
To implement an Iterator:
1) We need to implement next() and hasNext()
2) remove() is a default method in Java 8
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
The trick is that
an iterator pulls the date from a source
an observable pushes the data to callbacks
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
The trick is that
an iterator pulls the date from a source
an observable pushes the data to callbacks
So we need an adapter…
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
How has it been done in the JDK?
public static<T> Iterator<T>
iterator(Spliterator<? extends T> spliterator) {
class Adapter implements Iterator<T>, Consumer<T> {
// implementation
}
return new Adapter() ;
}
@JosePaumard#Devoxx #J8Stream
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 ;
}
}
}
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
Let us adapt this pattern!
public static<T> Iterator<T>
of(Observable<? extends T> observable) {
class Adapter implements Iterator<T>, Consumer<T> {
// implementation
}
return new Adapter() ;
}
@JosePaumard#Devoxx #J8Stream
class Adapter implements Iterator<T>, Consumer<T> {
boolean valueReady = false ;
T nextElement;
public void accept(T t) { // needs to get the data from the Observable
valueReady = true ;
nextElement = t ;
}
public boolean hasNext() {
return valueReady ;
}
public T next() {
if (!valueReady && !hasNext())
throw new NoSuchElementException() ;
else {
valueReady = false ;
return nextElement ;
}
}
}
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
The accept method
class 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
) ;
}
}
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
The accept method
class 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
) ;
}
}
final…
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
We can wrap those value in Atomic variable
class 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
) ;
}
}
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
Cant we do better?
interface Wrapper<E> {
E get() ;
}
Wrapper<Boolean> wb = () -> true ;
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
Cant we do better?
interface Wrapper<E> {
E get() ;
}
Wrapper<Boolean> wb = () -> true ;
Action1<Boolean> onNext = b -> wb.set(b) ; // should return Wrapper<T>
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
Cant we do better?
interface Wrapper<E> {
E get() ;
public default Wrapper<E> set(E e) {
// should return a wrapper of e
}
}
Wrapper<Boolean> wb = () -> true ;
Action1<Boolean> onNext = b -> wb.set(b) ; // should return Wrapper<T>
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
Cant we do better?
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>
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
We can wrap those value in Atomic variable
class 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
) ;
}
}
@JosePaumard#Devoxx #J8Stream
Iterating on an Observable
So we can build an Iterator on an Observable
And with it, a Spliterator on an Observable
it will work on cold observables
@JosePaumard#Devoxx #J8Stream
Getting the best of both API
The cold observables can be implemeted with Java 8 Streams
The hot observables can be implemented by combining both
API
Comparisons:
Patterns &
Performances
@JosePaumard#Devoxx #J8Stream
Let us do some comparisons
Let us take one use case
Implemented in Rx and J8 Streams
See the different ways of writing the same processings
And compare the processing times using JMH
@JosePaumard#Devoxx #J8Stream
Shakespeare plays Scrabble
Published in Java Magazine
Presented in Devoxx 2014
Used during Virtual Technology Summit
https://community.oracle.com/docs/DOC-916777
https://github.com/JosePaumard/jdk8-lambda-tour
@JosePaumard#Devoxx #J8Stream
Shakespeare plays Scrabble
Basically, we have the set of the words used by Shakespeare
The official Scrabble player dictionnary
And the question is: how good at Scrabble Shakespeare would
have been?
@JosePaumard#Devoxx #J8Stream
Shakespeare plays Scrabble
1) Build the histogram of the letters of a word
2) Number of blanks needed for a letter in a word
3) Number of blanks needed to write a word
4) Predicate to check is a word can be written with 2 blanks
5) Bonus for a doubled letter
6) Final score of a word
7) Histogram of the scores
8) Best word
@JosePaumard#Devoxx #J8Stream
1) Histogram of the letters
Java 8 Stream API – 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()
)
) ;
@JosePaumard#Devoxx #J8Stream
1) Histogram of the letters
Java 8 Stream API – 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()) ;
}) ;
@JosePaumard#Devoxx #J8Stream
1) Histogram of the letters
Java 8 Stream API – 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() ;
}
}
@JosePaumard#Devoxx #J8Stream
2) # of blanks for a letter
Java 8 Stream API – 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']
) ;
@JosePaumard#Devoxx #J8Stream
2) # of blanks for a letter
Java 8 Stream API – 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']
@JosePaumard#Devoxx #J8Stream
3) # of blanks for a word
Java 8 Stream API – Rx Java
// number of blanks for a given word
Function<String, Long> nBlanks =
word -> histoOfLetters.apply(word)
.entrySet().stream()
.mapToLong(blank)
.sum();
@JosePaumard#Devoxx #J8Stream
3) # of blanks for a word
Java 8 Stream API – 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) ;
@JosePaumard#Devoxx #J8Stream
4) Predicate for 2 blanks
Java 8 Stream API – 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?
Func1<String, Observable<Boolean>> checkBlanks =
word -> nBlanks.call(word)
.flatMap(l -> Observable.just(l <= 2L)) ;
@JosePaumard#Devoxx #J8Stream
5) Bonus for a doubled letter – 1
Java 8 Stream API – 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);
@JosePaumard#Devoxx #J8Stream
5) Bonus for a doubled letter – 1
Java 8 Stream API – 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()
)
) ;
@JosePaumard#Devoxx #J8Stream
5) Bonus for a doubled letter – 2
Java 8 Stream API – Rx Java
// Bonus for double letter
ToIntFunction<String> bonusForDoubleLetter =
word -> Stream.of(first3.apply(word), last3.apply(word))
.flatMapToInt(Function.identity())
.map(scoreOfALetter)
.max()
.orElse(0) ;
@JosePaumard#Devoxx #J8Stream
5) Bonus for a doubled letter – 2
Java 8 Stream API – Rx Java
// Bonus for double letter
Func1<String, Observable<Integer>> bonusForDoubleLetter =
word -> Observable.just(first3.call(word), last3.call(word))
.flatMap(observable -> observable)
.flatMap(scoreOfALetter)
.reduce(Integer::max) ;
@JosePaumard#Devoxx #J8Stream
6) Final score of a word
Java 8 Stream API – 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);
@JosePaumard#Devoxx #J8Stream
6) Final score of a word
Java 8 Stream API – 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) ;
@JosePaumard#Devoxx #J8Stream
7) Histogram of the scores
Java 8 Stream API – Rx Java
Function<Function<String, Integer>, Map<Integer, List<String>>>
buildHistoOnScore =
score -> shakespeareWords.stream().parallel()
.filter(scrabbleWords::contains)
.filter(checkBlanks)
.collect(
Collectors.groupingBy(
score,
() -> new TreeMap<>(Comparator.reverseOrder()),
Collectors.toList()
)
) ;
@JosePaumard#Devoxx #J8Stream
7) Histogram of the scores
Java 8 Stream API – Rx Java
Func1<Func1<String, Observable<Integer>>, Observable<TreeMap<Integer, List<String>>>>
buildHistoOnScore =
score -> Observable.from(() -> shakespeareWords.iterator())
.filter(scrabbleWords::contains)
.filter(word -> checkBlanks.call(word).toBlocking().first())
.collect(
() -> new TreeMap<Integer,
List<String>>(Comparator.reverseOrder()),
(TreeMap<Integer, List<String>> map, String word) -> {
Integer key = score.call(word).toBlocking().first() ;
List<String> list = map.get(key) ;
if (list == null) {
list = new ArrayList<String>() ;
map.put(key, list) ;
}
list.add(word) ;
}) ;
@JosePaumard#Devoxx #J8Stream
8) Best word
Java 8 Stream API – Rx Java
// best key / value pairs
List<Entry<Integer, List<String>>> finalList =
buildHistoOnScore.apply(score3).entrySet()
.stream()
.limit(3)
.collect(Collectors.toList()) ;
@JosePaumard#Devoxx #J8Stream
8) Best word
Java 8 Stream API – Rx Java
// best key / value pairs
List<Entry<Integer, List<String>>> finalList2 =
buildHistoOnScore.call(score3)
.flatMap(map -> Observable.from(() ->
map.entrySet().iterator()))
.take(3)
.collect(
() -> new ArrayList<Entry<Integer, List<String>>>(),
(list, entry) -> { list.add(entry) ; }
)
.toBlocking()
.first() ;
@JosePaumard#Devoxx #J8Stream
8) Best word
Java 8 Stream API – Rx Java
// best key / value pairs
CountDownLatch latch = new CountDownLatch(3) ;
List<Entry<Integer, List<String>>> finalList2 =
buildHistoOnScore.call(score3)
.flatMap(map -> Observable.from(() ->
map.entrySet().iterator()))
.take(3)
.collect(
() -> new ArrayList<Entry<Integer, List<String>>>(),
(list, entry) -> { list.add(entry) ; latch.countDown() ; }
)
.forEach(...) ;
latch.await() ;
@JosePaumard#Devoxx #J8Stream
Patterns comparison
Java 8 Stream API: clean, simple, factory methods for
Collectors
RxJava: flatMap calls, lack of factory methods
Java 8 can easily go parallel, which is a plus
@JosePaumard#Devoxx #J8Stream
Performances
Let us use JMH
Standard tool for measuring code performance on the JVM
Developed as an Open JDK tool
By Aleksey Shipilev http://shipilev.net/
https://twitter.com/shipilev
http://openjdk.java.net/projects/code-tools/jmh/
http://openjdk.java.net/projects/code-tools/jcstress/
https://www.parleys.com/tutorial/java-microbenchmark-harness-the-lesser-two-evils
@JosePaumard#Devoxx #J8Stream
JMH
Easy to setup
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.11.1</version>
</dependency>
@JosePaumard#Devoxx #J8Stream
JMH
Easy to use
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations=5)
@Measurement(iterations=5)
@Fork(3)
public List<Entry<Integer, List<String>>> measureAverage() {
// implementation to test
}
@JosePaumard#Devoxx #J8Stream
JMH
Launching a benchmark
> mvn clean install
> java –jar target/benchmark.jar
@JosePaumard#Devoxx #J8Stream
JMH
3 ways of measuring performances
average execution time
number of executions per second
quantiles diagrams
@JosePaumard#Devoxx #J8Stream
Performances
Average execution time
Benchmark Mode Cnt Score Error Units
NonParallelStreams avgt 100 29,027 ± 0,279 ms/op
@JosePaumard#Devoxx #J8Stream
Performances
Average execution time
Benchmark Mode Cnt Score Error Units
NonParallelStreams avgt 100 29,027 ± 0,279 ms/op
RxJava avgt 100 253,788 ± 1,421 ms/op
@JosePaumard#Devoxx #J8Stream
Performances
Average execution time
Benchmark Mode Cnt Score Error Units
NonParallelStreams avgt 100 29,027 ± 0,279 ms/op
RxJava avgt 100 253,788 ± 1,421 ms/op
ParallelStreams avgt 100 7,624 ± 0,055 ms/op
@JosePaumard#Devoxx #J8Stream
Performances
Average execution time
RxJava spends a lot of time openning observables, due to the
all flatMap patterns
Benchmark Mode Cnt Score Error Units
NonParallelStreams avgt 100 29,027 ± 0,279 ms/op
RxJava avgt 100 253,788 ± 1,421 ms/op
ParallelStreams avgt 100 7,624 ± 0,055 ms/op
@JosePaumard#Devoxx #J8Stream
Performances
The bench is on Github:
https://github.com/JosePaumard/jdk8-stream-rx-comparison
What about
Java 9?
@JosePaumard#Devoxx #J8Stream
Java 8 Stream API
Back to the definitions:
1) A Stream does not hold any data
2) A Stream does not modify its data
@JosePaumard#Devoxx #J8Stream
Java 8 Stream API
Back to the definitions:
1) A Stream does not hold any data
2) A Stream does not modify its data
How does a Stream work?
1) It connects to a source of data: one source = one stream
2) It consumes the data from the source: « pull mode »
@JosePaumard#Devoxx #J8Stream
Java 8 Stream API
What about:
- Connecting several streams to a single source?
@JosePaumard#Devoxx #J8Stream
Java 8 Stream API
What about:
- Connecting several streams to a single source?
- Connecting several sources to a single stream?
@JosePaumard#Devoxx #J8Stream
Java 8 Stream API
What about:
- Connecting several streams to a single source?
- Connecting several sources to a single stream?
- Having a source that produces data whether or not a stream
is connected to it
@JosePaumard#Devoxx #J8Stream
Java 8 Stream API
What about:
- Connecting several streams to a single source?
- Connecting several sources to a single stream?
- Having a source that produces data whether or not a stream
is connected to it
Clearly, the Stream API has not been made to handle this
@JosePaumard#Devoxx #J8Stream
Reactive Stream API
This leads to the « reactive stream » API
3rd party API: Rx Java (and several other languages)
Implementations available as a preview of JDK 9
Everything takes place in java.util.concurrent.Flow
Available in the current JDK 9
@JosePaumard#Devoxx #J8Stream
Push mode stream
Let us write a model for the source of data
public interface Publisher<T> {
public ... subscribe(Subscriber<T> subscriber);
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
Let us write a model for the source of data
As a subscriber I will want to unsubscribe
So I need an object from the publisher
on which I can call cancel()
public interface Publisher<T> {
public ... subscribe(Subscriber<T> subscriber);
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
Let us write a model for the source of data
The first idea that could come to mind is to return a
Subscription object
public interface Publisher<T> {
public Subscription subscribe(Subscriber<T> subscriber);
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
Let us write a model for the source of data
But it will be a callback, to stay in an asynchronous world
public interface Publisher<T> {
public void subscribe(Subscriber<T> subscriber);
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
Callback in the subscriber to get a subscription
public interface Subscriber<T> {
public void onSubscribe(Subscription subscription);
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
Callback in the subscriber to get a subscription
public interface Subscriber<T> {
public void onSubscribe(Subscription subscription);
}
public interface Subscription {
public void cancel();
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
The publisher might look like this
public class SimplePublisher<T> implements Publisher<T> {
private Set<Subscriber<T>> subs = ConcurrentHashMap.newKeySet();
public void subscribe(Subscriber<T> subscriber) {
if (subs.add(subscriber)) {
Subscription subscription = new SimpleSubscription();
subscriber.onSubscribe(subscription);
}
}
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
In the subscribing code
public class SimpleSubscriber<T> implements Subscriber<T> {
private Subscription subscription;
@Override
public void onSubscribe(Subscription subscription) {
this.subscription = subscription;
}
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
In the running code
Publisher<String> publisher = ...;
Subscriber<String> subscriber = ...;
publisher.subscribe(subscriber);
// some more code
subscriber.getSubscription().cancel();
@JosePaumard#Devoxx #J8Stream
Push mode stream
Callback in the subscriber to get a subscription
I also need callbacks to get the data itself
public interface Subscriber<T> {
public void onSubscribe(Subscription subscription);
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
Callback in the subscriber to get a subscription
public interface Subscriber<T> {
public void onSubscribe(Subscription subscription);
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
Callback in the subscriber to get a subscription
public interface Subscriber<T> {
public void onSubscribe(Subscription subscription);
public void onNext(T item);
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
Callback in the subscriber to get a subscription
public interface Subscriber<T> {
public void onSubscribe(Subscription subscription);
public void onNext(T item);
public void onComplete();
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
Callback in the subscriber to get a subscription
public interface Subscriber<T> {
public void onSubscribe(Subscription subscription);
public void onNext(T item);
public void onComplete();
public void onError(Throwable throwable);
}
@JosePaumard#Devoxx #J8Stream
Push mode stream
Having a source that produces data independantly from its
consumers implies to work in an asynchronous mode
The API is built on callbacks
@JosePaumard#Devoxx #J8Stream
Several streams per source
In « pull mode », it would not work, or would require the
streams to be synchronized
map Filter 1 Filter 2 Average
map Filter 1 Histogram
Data
@JosePaumard#Devoxx #J8Stream
Several streams per source
In « pull mode », it would not work, or would require the
streams to be synchronized
In « push mode », it does not raise any problem
map Filter 1 Filter 2 Average
map Filter 1 Histogram
Data
@JosePaumard#Devoxx #J8Stream
Several sources for a stream
In « pull mode », it requires a special Spliterator
map Filter 1 Filter 2 Average
Data 1
Data 2
@JosePaumard#Devoxx #J8Stream
Several sources for a stream
In « pull mode », it requires a special Spliterator
In « push mode », since both sources are not synchronized,
we may have problems
map Filter 1 Filter 2 Average
Data 1
Data 2
@JosePaumard#Devoxx #J8Stream
Push mode with several sources
At some point in our data processing pipeline we want to see
both sources as one, ie merged in some way
@JosePaumard#Devoxx #J8Stream
Push mode with several sources
At some point in our data processing pipeline we want to see
both sources as one, ie merged in some way
How can we merge them if one source is faster than the other?
@JosePaumard#Devoxx #J8Stream
Push mode with several sources
At some point in our data processing pipeline we want to see
both sources as one, ie merged in some way
How can we merge them if one source is faster than the other?
We have all the strategies defined in RxJava
@JosePaumard#Devoxx #J8Stream
Merging sources in push mode
1) Decide to follow one of the streams, the first one
2) Combine the two last seen items,
- everytime a new item is generated
- or synchronized on a clock, or another source
@JosePaumard#Devoxx #J8Stream
Merging sources in push mode
1) Decide to follow one of the streams, the first one
2) Combine the two last seen items,
- everytime a new item is generated
- or synchronized on a clock, or another source
Sampler, Debouncer, Throttler, …
@JosePaumard#Devoxx #J8Stream
The question of backpressure
What will happen if a source is « too fast »?
That is, a consumer cannot process data fast enough
It leads to the same question of « backpressure »
@JosePaumard#Devoxx #J8Stream
Backpressure
Several strategies:
1) Create a buffer
2) Synchronize on a clock, or a gate, that could be generated
by the slow observer and sample, or windows, or
debounce, or…
3) Try to slow down the source (can be done if I have the
hand on both the producer and the consumer)
@JosePaumard#Devoxx #J8Stream
Backpressure
There is code for that in the Subscription object
The request() method is there to give information to the
producer
public interface Subscription {
public void cancel();
public void request(long n);
}
@JosePaumard#Devoxx #J8Stream
Backpressure
There is code for that in the Subscription object
The request() method is there to give information to the
producer
public void onNext(String element) {
// process the element
this.subscription.request(1L);
}
@JosePaumard#Devoxx #J8Stream
Backpressure
Several strategies:
1) Create a buffer
2) Synchronize on a clock, or a gate, that could be generated
by the slow observer and sample, or windows, or
debounce, or…
3) Try to slow down the source (can be done if I have the
hand on both the producer and the consumer)
4) Have several observers in parallel and then merge the
results
@JosePaumard#Devoxx #J8Stream
Reactive Streams
New concept (at least in the JDK)
New complexity, several use cases are possible
Still under work (in the JDK and in 3rd party)
@JosePaumard#Devoxx #J8Stream
Reactive Streams links
Some references on the reactive streams:
http://www.reactive-streams.org/
http://reactivex.io/
https://github.com/reactive-streams/
http://openjdk.java.net/jeps/266
http://gee.cs.oswego.edu/dl/jsr166/dist/docs/index.html
@JosePaumard#Devoxx #J8Stream
Reactive Streams links
In the classes currently available in the JSR 166 package:
- The class Flow has the Publisher, Subscriber and
Subscription interfaces, and the Processor interface
- The class SubmissionPublisher, that implements Publisher,
meant to be overriden or used as a component of a complete
implementation
Just made it to the OpenJDK 9
Conclusion
@JosePaumard#Devoxx #J8Stream
Conclusion
Functional programming is in the mood
From the pure performance point of view, things are not that
simple
Java 8 Streams have adopted a partial functional approach,
which is probably a good trade off
@JosePaumard#Devoxx #J8Stream
Conclusion
Java 8 Stream API: great API to process data in a « pull »
mode
The introduction of a « push » mode allows for many
improvements (synchronization, backpressure)
The backpressure question is relevant
Loosing items ≠ loosing information!
@JosePaumard#Devoxx #J8Stream
Conclusion
RxJava: rich and complex API
many patterns are available in Java 8 Streams
can run in Java 7 applications
the « push » approach is very interesting
choose your use case carefully, to avoir performance hits
http://www.reactive-streams.org/
http://reactivex.io/
https://github.com/reactive-streams/
@JosePaumard#Devoxx #J8Stream
Conclusion
Java 8 Streams: part of the JDK
good performances, efficient memory footprint
parallelization
extensible through the Spliterator patterns
@JosePaumard#Devoxx #J8Stream
Conclusion
Streams & Reactive Streams are very active topics
Java Stream has been released with Java 8, improvements
will be added in Java 9 and beyond
Reactive Streams has several 3rd party implementations
(RxJava) in several languages
Will be part of Java 9
Thank you
Q/A

More Related Content

PDF
Free your lambdas
PDF
Going reactive in java
PDF
Java 8 Stream API and RxJava Comparison
PDF
Java 8, Streams & Collectors, patterns, performances and parallelization
PDF
Free your lambdas
PDF
Java 8 Stream API (Valdas Zigas)
PDF
Linked to ArrayList: the full story
PDF
JDK8 : parallel programming made (too ?) easy
Free your lambdas
Going reactive in java
Java 8 Stream API and RxJava Comparison
Java 8, Streams & Collectors, patterns, performances and parallelization
Free your lambdas
Java 8 Stream API (Valdas Zigas)
Linked to ArrayList: the full story
JDK8 : parallel programming made (too ?) easy

What's hot (20)

PDF
Asynchronous API in Java8, how to use CompletableFuture
PDF
Java SE 8 for Java EE developers
PDF
Autumn collection JavaOne 2014
PDF
The Sincerest Form of Flattery
PDF
Java SE 8 for Java EE developers
PDF
Java 8 Streams & Collectors : the Leuven edition
PPTX
JFokus 50 new things with java 8
PPTX
The Sincerest Form of Flattery
PDF
Collectors in the Wild
PDF
Lambdas and Streams Master Class Part 2
PDF
Lambda and Stream Master class - part 1
PDF
WebTech Tutorial Querying DBPedia
PDF
Jsonsaga 100605143125-phpapp02
PPTX
SPARQL Cheat Sheet
PDF
Core Java - Quiz Questions - Bug Hunt
PDF
Linking the world with Python and Semantics
PDF
Automatically generating-json-from-java-objects-java-objects268
PPTX
Semantic web meetup – sparql tutorial
PDF
Json the-x-in-ajax1588
PPT
Twinkle: A SPARQL Query Tool
Asynchronous API in Java8, how to use CompletableFuture
Java SE 8 for Java EE developers
Autumn collection JavaOne 2014
The Sincerest Form of Flattery
Java SE 8 for Java EE developers
Java 8 Streams & Collectors : the Leuven edition
JFokus 50 new things with java 8
The Sincerest Form of Flattery
Collectors in the Wild
Lambdas and Streams Master Class Part 2
Lambda and Stream Master class - part 1
WebTech Tutorial Querying DBPedia
Jsonsaga 100605143125-phpapp02
SPARQL Cheat Sheet
Core Java - Quiz Questions - Bug Hunt
Linking the world with Python and Semantics
Automatically generating-json-from-java-objects-java-objects268
Semantic web meetup – sparql tutorial
Json the-x-in-ajax1588
Twinkle: A SPARQL Query Tool
Ad

Viewers also liked (20)

PDF
Déploiement d'une application Java EE dans Azure
PPTX
Mini training - Reactive Extensions (Rx)
PDF
RxJava 2.0 介紹
PDF
Rxjava 介紹與 Android 中的 RxJava
PDF
RxJava - introduction & design
PDF
Building Scalable Stateless Applications with RxJava
PDF
Phoenix for Rubyists
PPTX
Hazelcast and MongoDB at Cloud CMS
PDF
Async Gateway или Разработка системы распределенных вычислений с нуля
PPT
HighLoad++ 2009 In-Memory Data Grids
PDF
50 nouvelles choses que l'on peut faire en Java 8
PDF
Алексей Николаенков, Devexperts
PPTX
Code review at large scale
PDF
Hazelcast for Terracotta Users
PDF
Amazon cloud – готовим вместе
PDF
ЖК Зорге 9
PDF
50 new things we can do with Java 8
PPTX
Gamification in outsourcing company: experience report.
PPTX
Java 8, the Good, the Bad and the Ugly
PDF
Очень вкусный фрукт Guava
Déploiement d'une application Java EE dans Azure
Mini training - Reactive Extensions (Rx)
RxJava 2.0 介紹
Rxjava 介紹與 Android 中的 RxJava
RxJava - introduction & design
Building Scalable Stateless Applications with RxJava
Phoenix for Rubyists
Hazelcast and MongoDB at Cloud CMS
Async Gateway или Разработка системы распределенных вычислений с нуля
HighLoad++ 2009 In-Memory Data Grids
50 nouvelles choses que l'on peut faire en Java 8
Алексей Николаенков, Devexperts
Code review at large scale
Hazelcast for Terracotta Users
Amazon cloud – готовим вместе
ЖК Зорге 9
50 new things we can do with Java 8
Gamification in outsourcing company: experience report.
Java 8, the Good, the Bad and the Ugly
Очень вкусный фрукт Guava
Ad

Similar to Java 8 Streams and Rx Java Comparison (20)

PDF
Querying data on the Web – client or server?
PPTX
Spring Boot and REST API
PDF
Writing a REST Interconnection Library in Swift
PPTX
PDF
ApacheCon 2000 Everything you ever wanted to know about XML Parsing
PDF
Lambdas: Myths and Mistakes
PDF
Sustainable queryable access to Linked Data
PPT
Hatkit Project - Datafiddler
ODP
REST dojo Comet
PDF
The Power of RxJS in Nativescript + Angular
PDF
Arabidopsis Information Portal, Developer Workshop 2014, Introduction
PPT
Lecture 3 - Comm Lab: Web @ ITP
PDF
Full-Stack Data Science: How to be a One-person Data Team
PPTX
Java8.part2
PDF
Engage 2023: Taking Domino Apps to the next level by providing a Rest API
PDF
Lettering js
PDF
Sensor thingsapi webinar-#3-rest-for-iot-api-20151210
PPT
NetBase API Presentation
PDF
IQPC Canada XML 2001: How to Use XML Parsing to Enhance Electronic Communication
Querying data on the Web – client or server?
Spring Boot and REST API
Writing a REST Interconnection Library in Swift
ApacheCon 2000 Everything you ever wanted to know about XML Parsing
Lambdas: Myths and Mistakes
Sustainable queryable access to Linked Data
Hatkit Project - Datafiddler
REST dojo Comet
The Power of RxJS in Nativescript + Angular
Arabidopsis Information Portal, Developer Workshop 2014, Introduction
Lecture 3 - Comm Lab: Web @ ITP
Full-Stack Data Science: How to be a One-person Data Team
Java8.part2
Engage 2023: Taking Domino Apps to the next level by providing a Rest API
Lettering js
Sensor thingsapi webinar-#3-rest-for-iot-api-20151210
NetBase API Presentation
IQPC Canada XML 2001: How to Use XML Parsing to Enhance Electronic Communication

More from José Paumard (17)

PDF
Loom Virtual Threads in the JDK 19
PDF
From Java 11 to 17 and beyond.pdf
PDF
The Future of Java: Records, Sealed Classes and Pattern Matching
PDF
Deep Dive Java 17 Devoxx UK
PDF
Designing functional and fluent API: application to some GoF patterns
PDF
Designing functional and fluent API: example of the Visitor Pattern
PDF
Construire son JDK en 10 étapes
PDF
Java Keeps Throttling Up!
PDF
Asynchronous Systems with Fn Flow
PDF
Java Full Throttle
PDF
JAX-RS and CDI Bike the (Reactive) Bridge
PDF
Streams in the wild
PDF
JAX RS and CDI bike the reactive bridge
PDF
L'API Collector dans tous ses états
PDF
ArrayList et LinkedList sont dans un bateau
PDF
API Asynchrones en Java 8
PDF
Les Streams sont parmi nous
Loom Virtual Threads in the JDK 19
From Java 11 to 17 and beyond.pdf
The Future of Java: Records, Sealed Classes and Pattern Matching
Deep Dive Java 17 Devoxx UK
Designing functional and fluent API: application to some GoF patterns
Designing functional and fluent API: example of the Visitor Pattern
Construire son JDK en 10 étapes
Java Keeps Throttling Up!
Asynchronous Systems with Fn Flow
Java Full Throttle
JAX-RS and CDI Bike the (Reactive) Bridge
Streams in the wild
JAX RS and CDI bike the reactive bridge
L'API Collector dans tous ses états
ArrayList et LinkedList sont dans un bateau
API Asynchrones en Java 8
Les Streams sont parmi nous

Recently uploaded (20)

PPTX
COMPUTERS AS DATA ANALYSIS IN PRECLINICAL DEVELOPMENT.pptx
PPTX
Onica Farming 24rsclub profitable farm business
PPTX
Open Quiz Monsoon Mind Game Prelims.pptx
PDF
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
PPTX
The Healthy Child – Unit II | Child Health Nursing I | B.Sc Nursing 5th Semester
PDF
STATICS OF THE RIGID BODIES Hibbelers.pdf
PPTX
Renaissance Architecture: A Journey from Faith to Humanism
PDF
Electrolyte Disturbances and Fluid Management A clinical and physiological ap...
PPTX
Pharma ospi slides which help in ospi learning
PDF
O7-L3 Supply Chain Operations - ICLT Program
PDF
From loneliness to social connection charting
PDF
Saundersa Comprehensive Review for the NCLEX-RN Examination.pdf
PDF
Origin of periodic table-Mendeleev’s Periodic-Modern Periodic table
PPTX
IMMUNITY IMMUNITY refers to protection against infection, and the immune syst...
PDF
Business Ethics Teaching Materials for college
PDF
Module 3: Health Systems Tutorial Slides S2 2025
PDF
Physiotherapy_for_Respiratory_and_Cardiac_Problems WEBBER.pdf
PPTX
human mycosis Human fungal infections are called human mycosis..pptx
PPTX
Cardiovascular Pharmacology for pharmacy students.pptx
PDF
Mark Klimek Lecture Notes_240423 revision books _173037.pdf
COMPUTERS AS DATA ANALYSIS IN PRECLINICAL DEVELOPMENT.pptx
Onica Farming 24rsclub profitable farm business
Open Quiz Monsoon Mind Game Prelims.pptx
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
The Healthy Child – Unit II | Child Health Nursing I | B.Sc Nursing 5th Semester
STATICS OF THE RIGID BODIES Hibbelers.pdf
Renaissance Architecture: A Journey from Faith to Humanism
Electrolyte Disturbances and Fluid Management A clinical and physiological ap...
Pharma ospi slides which help in ospi learning
O7-L3 Supply Chain Operations - ICLT Program
From loneliness to social connection charting
Saundersa Comprehensive Review for the NCLEX-RN Examination.pdf
Origin of periodic table-Mendeleev’s Periodic-Modern Periodic table
IMMUNITY IMMUNITY refers to protection against infection, and the immune syst...
Business Ethics Teaching Materials for college
Module 3: Health Systems Tutorial Slides S2 2025
Physiotherapy_for_Respiratory_and_Cardiac_Problems WEBBER.pdf
human mycosis Human fungal infections are called human mycosis..pptx
Cardiovascular Pharmacology for pharmacy students.pptx
Mark Klimek Lecture Notes_240423 revision books _173037.pdf

Java 8 Streams and Rx Java Comparison

  • 2. Why should we be interested in the Streams API?
  • 4. @JosePaumard#Devoxx #J8Stream It’s all about data processing Up to 2014: only one tool, the Collection Framework Also 3rd party API: Common Collections, …
  • 5. @JosePaumard#Devoxx #J8Stream It’s all about data processing Up to 2014: only one tool, the Collection Framework Also 3rd party API: Common Collections, … From 2014: so many new APIs
  • 6. @JosePaumard#Devoxx #J8Stream It’s all about data processing Why so? Because data processing is more and more important
  • 7. @JosePaumard#Devoxx #J8Stream It’s all about data processing Why so? Because data processing is more and more important and more and more complex!
  • 8. @JosePaumard#Devoxx #J8Stream It’s all about data processing Why so? Because data processing is more and more important and more and more complex! bigger and bigger amount of data to process
  • 9. @JosePaumard#Devoxx #J8Stream It’s all about data processing Why so? Because data processing is more and more important and more and more complex! bigger and bigger amount of data to process controlled response time
  • 10. @JosePaumard#Devoxx #J8Stream It’s all about data processing Why so? Because data processing is more and more important and more and more complex! bigger and bigger amount of data to process controlled response time complex algorithms
  • 11. @JosePaumard#Devoxx #J8Stream It’s all about data processing So data processing needs high level primitives
  • 12. @JosePaumard#Devoxx #J8Stream It’s all about data processing So data processing needs high level primitives Able to access data wherever it is
  • 13. @JosePaumard#Devoxx #J8Stream It’s all about data processing So data processing needs high level primitives Able to access data wherever it is That provides map / filter / reduce functions
  • 14. @JosePaumard#Devoxx #J8Stream It’s all about data processing So data processing needs high level primitives Able to access data wherever it is That provides map / filter / reduce functions And efficient implementations of those!
  • 15. @JosePaumard#Devoxx #J8Stream It’s all about data processing So data processing needs high level primitives Able to access data wherever it is That provides map / filter / reduce functions And efficient implementations of those! Most probably implemented in parallel
  • 16. @JosePaumard#Devoxx #J8Stream Agenda 1) To present two API: the Java 8 Stream API and RxJava • Fundamentals • Implemented functionalities • Patterns!
  • 17. @JosePaumard#Devoxx #J8Stream Agenda 1) To present two API: the Java 8 Stream API and RxJava • Fundamentals • Implemented functionalities • Patterns! 2) And to compare those APIs • From the developer point of view • Performances!
  • 22. @JosePaumard#Devoxx #J8Stream API Stream What is a Java 8 Stream? An object that connects to a source
  • 23. @JosePaumard#Devoxx #J8Stream API Stream What is a Java 8 Stream? An object that connects to a source That does not hold any data
  • 24. @JosePaumard#Devoxx #J8Stream API Stream What is a Java 8 Stream? An object that connects to a source That does not hold any data That implements the map / filter / reduce pattern
  • 25. @JosePaumard#Devoxx #J8Stream API Stream What is a Java 8 Stream? An object that connects to a source That does not hold any data That implements the map / filter / reduce pattern A new concept in JDK 8
  • 26. @JosePaumard#Devoxx #J8Stream Definition of a Stream Two things about streams: 1) A Stream does not hold any data 2) A Stream does not modify the data it gets from the source
  • 27. @JosePaumard#Devoxx #J8Stream API Stream Example List<String> list = Arrays.asList("one", "two", "three") ; list.stream() .map(s -> s.toUpperCase()) .max(Comparator.comparing(s -> s.length())) .ifPresent(s -> System.out.println(s)) ;
  • 28. @JosePaumard#Devoxx #J8Stream API Stream Example List<String> list = Arrays.asList("one", "two", "three") ; list.stream() // creation of a new Stream object .map(s -> s.toUpperCase()) .max(Comparator.comparing(s -> s.length())) .ifPresent(s -> System.out.println(s)) ;
  • 29. @JosePaumard#Devoxx #J8Stream API Stream Example List<String> list = Arrays.asList("one", "two", "three") ; list.stream() .map(s -> s.toUpperCase()) // to upper case .filter(s -> s.length() < 20) .max(Comparator.comparing(s -> s.length())) .ifPresent(s -> System.out.println(s)) ;
  • 30. @JosePaumard#Devoxx #J8Stream API Stream Example List<String> list = Arrays.asList("one", "two", "three") ; list.stream() .map(s -> s.toUpperCase()) .filter(s -> s.length() < 20) // remove strings longer than 20 .max(Comparator.comparing(s -> s.length())) .ifPresent(s -> System.out.println(s)) ;
  • 31. @JosePaumard#Devoxx #J8Stream API Stream Example List<String> list = Arrays.asList("one", "two", "three") ; list.stream() .map(s -> s.toUpperCase()) .filter(s -> s.length() < 20) .max(Comparator.comparing(s -> s.length())) // take the longest s .ifPresent(s -> System.out.println(s)) ;
  • 32. @JosePaumard#Devoxx #J8Stream API Stream Example List<String> list = Arrays.asList("one", "two", "three") ; list.stream() .map(s -> s.toUpperCase()) .filter(s -> s.length() < 20) .max(Comparator.comparing(s -> s.length())) .ifPresent(s -> System.out.println(s)) ; // and print the result
  • 33. @JosePaumard#Devoxx #J8Stream API Stream Example List<String> list = Arrays.asList("one", "two", "three") ; list.stream() .map(String::toUpperCase) .filter(s -> s.length() < 20) .max(Comparator.comparing(String::length)) .ifPresent(System.out::println) ; // and print the result
  • 34. @JosePaumard#Devoxx #J8Stream Collectors We can use collectors List<Person> list = ... ; list.stream() .filter(person -> person.getAge() > 30) .collect( Collectors.groupingBy( Person::getAge, // key extractor Collectors.counting() // downstream collector ) ) ;
  • 35. @JosePaumard#Devoxx #J8Stream Collectors We can use collectors List<Person> list = ... ; Map< list.stream() .filter(person -> person.getAge() > 30) .collect( Collectors.groupingBy( Person::getAge, // key extractor Collectors.counting() // downstream collector ) ) ;
  • 36. @JosePaumard#Devoxx #J8Stream Collectors We can use collectors List<Person> list = ... ; Map<Integer, list.stream() .filter(person -> person.getAge() > 30) .collect( Collectors.groupingBy( Person::getAge, // key extractor Collectors.counting() // downstream collector ) ) ;
  • 37. @JosePaumard#Devoxx #J8Stream Collectors We can use collectors List<Person> list = ... ; Map<Integer, Long> map = list.stream() .filter(person -> person.getAge() > 30) .collect( Collectors.groupingBy( Person::getAge, // key extractor Collectors.counting() // downstream collector ) ) ;
  • 38. @JosePaumard#Devoxx #J8Stream Collectors And we can go parallel! List<Person> list = ... ; Map<Integer, Long> map = list.stream().parallel() .filter(person -> person.getAge() > 30) .collect( Collectors.groupingBy( Person::getAge, // key extractor Collectors.counting() // downstream collector ) ) ;
  • 39. @JosePaumard#Devoxx #J8Stream Sources of a Stream Many ways of connecting a Stream to a source of data
  • 40. @JosePaumard#Devoxx #J8Stream Sources of a Stream First patterns to create a Stream List<Person> people = Arrays.asList(p1, p2, p3);
  • 41. @JosePaumard#Devoxx #J8Stream Sources of a Stream First patterns to create a Stream List<Person> people = Arrays.asList(p1, p2, p3); Stream<Person> stream = people.stream();
  • 42. @JosePaumard#Devoxx #J8Stream Sources of a Stream First patterns to create a Stream List<Person> people = Arrays.asList(p1, p2, p3); Stream<Person> stream = people.stream(); Stream<Person> stream = Stream.of(p1, p2, p3);
  • 43. @JosePaumard#Devoxx #J8Stream More patterns Stream on String String crazy = "supercalifragilisticexpialidocious"; IntStream letters = crazy.chars(); long count = letters.distinct().count();
  • 44. @JosePaumard#Devoxx #J8Stream More patterns Stream on String String crazy = "supercalifragilisticexpialidocious"; IntStream letters = crazy.chars(); long count = letters.distinct().count(); > count = 15
  • 45. @JosePaumard#Devoxx #J8Stream More patterns Stream on String String crazy = "supercalifragilisticexpialidocious"; IntStream letters = crazy.chars(); letters.boxed().collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) );
  • 46. @JosePaumard#Devoxx #J8Stream More patterns Stream on String String crazy = "supercalifragilisticexpialidocious"; IntStream letters = crazy.chars(); Map< letters.boxed().collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) );
  • 47. @JosePaumard#Devoxx #J8Stream More patterns Stream on String String crazy = "supercalifragilisticexpialidocious"; IntStream letters = crazy.chars(); Map<Integer, letters.boxed().collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) );
  • 48. @JosePaumard#Devoxx #J8Stream More patterns Stream on String String crazy = "supercalifragilisticexpialidocious"; IntStream letters = crazy.chars(); Map<Integer, Long> map = letters.boxed().collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) );
  • 49. @JosePaumard#Devoxx #J8Stream More patterns Stream on String String crazy = "supercalifragilisticexpialidocious"; IntStream letters = crazy.chars(); Map<Integer, Long> map = letters.boxed().collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) ); a -> 3 c -> 3 d -> 1 e -> 2 f -> 1 g -> 1 i -> 7 l -> 3 o -> 2 p -> 2 r -> 2 s -> 3 t -> 1 u -> 2 x -> 1
  • 50. @JosePaumard#Devoxx #J8Stream More patterns Stream built on the lines of a file String book = "alice-in-wonderland.txt"; Stream<String> lines = Files.lines(Paths.get(book)); // autocloseable
  • 51. @JosePaumard#Devoxx #J8Stream More patterns Stream built on the lines of a file Stream built the words of a String String book = "alice-in-wonderland.txt"; Stream<String> lines = Files.lines(Paths.get(book)); // autocloseable String line = "Alice was beginning to get very tired of"; Stream<String> words = Pattern.compile(" ").splitAsStream(line);
  • 52. @JosePaumard#Devoxx #J8Stream Flatmap How to mix both to get all the words of a book? Memory-efficient implementations built on lazy reading of the source of data Function<String, Stream<String>> splitToWords = line -> Pattern.compile(" ").splitAsStream(line); Stream<String> lines = Files.lines(path); Stream<String> words = lines.flatMap(splitToWords);
  • 53. @JosePaumard#Devoxx #J8Stream Flatmap Analyzing the result Stream<String> words = lines.flatMap(splitToWords); long count = words.filter(word -> word.length() > 2) .map(String::toLowerCase) .distinct() .count();
  • 54. @JosePaumard#Devoxx #J8Stream Flatmap Another analysis of the result Stream<String> words = lines.flatMap(splitToWords); Map<Integer, Long> map = words.filter(word -> word.length() > 2) .map(String::toLowerCase) // .distinct() .collect( Collectors.groupingBy( String::length, Collectors.counting() ) );
  • 55. @JosePaumard#Devoxx #J8Stream Extracting the max from a Map Yet another analysis of the result map.entrySet().stream() .sorted( Entry.<Integer, Long>comparingByValue().reversed() ) .limit(3) .forEach(System.out::println);
  • 56. @JosePaumard#Devoxx #J8Stream Connecting a Stream on a source Connecting a Stream on a non-standard source is possible
  • 57. @JosePaumard#Devoxx #J8Stream Connecting a Stream on a source Connecting a Stream on a non-standard source is possible A Stream is built on 2 things: - A Spliterator (comes from split and iterator) - A ReferencePipeline (the implementation)
  • 58. @JosePaumard#Devoxx #J8Stream Connecting a Stream on a source The Spliterator is meant to be overriden public interface Spliterator<T> { boolean tryAdvance(Consumer<? super T> action) ; Spliterator<T> trySplit() ; long estimateSize(); int characteristics(); }
  • 59. @JosePaumard#Devoxx #J8Stream Connecting a Stream on a source The Spliterator is meant to be overriden public interface Spliterator<T> { boolean tryAdvance(Consumer<? super T> action) ; Spliterator<T> trySplit(); // not needed for non-parallel operations long estimateSize(); // can return 0 int characteristics(); // returns a constant }
  • 60. @JosePaumard#Devoxx #J8Stream Examples of custom Spliterators Suppose we have a Stream: [1, 2, 3, 4, 5, …] We want to regroup the elements: [[1, 2, 3], [4, 5, 6], [7, 8, 9], …] Let us build a GroupingSpliterator to do that
  • 61. @JosePaumard#Devoxx #J8Stream GroupingSpliterator [1, 2, 3, 4, 5, …] -> [[1, 2, 3], [4, 5, 6], …] public class GroupingSpliterator<E> implements Spliterator<Stream<E>> { private final long grouping ; private final Spliterator<E> spliterator ; // implementation }
  • 62. @JosePaumard#Devoxx #J8Stream GroupingSpliterator [1, 2, 3, 4, 5, …] -> [[1, 2, 3], [4, 5, 6], …] public class GroupingSpliterator<E> implements Spliterator<Stream<E>> { private final long grouping ; private final Spliterator<E> spliterator ; // implementation } GroupingSpliterator<Integer> gs = new GroupingSpliterator(spliterator, grouping);
  • 63. @JosePaumard#Devoxx #J8Stream GroupingSpliterator Implementing estimateSize() and characteristics() public long estimateSize() { return spliterator.estimateSize() / grouping ; } public int characteristics() { return this.spliterator.characteristics(); }
  • 64. @JosePaumard#Devoxx #J8Stream GroupingSpliterator Implementing trySplit() public Spliterator<Stream<E>> trySplit() { Spliterator<E> spliterator = this.spliterator.trySplit() ; return new GroupingSpliterator<E>(spliterator, grouping) ; }
  • 65. @JosePaumard#Devoxx #J8Stream GroupingSpliterator Implementing tryAdvance() public boolean tryAdvance(Consumer<? super Stream<E>> action) { // should call action.accept() with the next element of the Stream // and return true if more elements are to be consumed return true ; // false when we are done }
  • 66. @JosePaumard#Devoxx #J8Stream GroupingSpliterator The structure of the resulting Stream is the following: [[1, 2, 3], [4, 5, 6], [7, 8, 9], …] Each element is a Stream built on the elements of the underlying Stream
  • 67. @JosePaumard#Devoxx #J8Stream GroupingSpliterator Build a Stream element by element is done with a Stream.Builder Stream.Builder<E> builder = Stream.builder() ; for (int i = 0 ; i < grouping ; i++) { spliterator.tryAdvance(element -> builder.add(element); } Stream<E> subStream = subBuilder.build(); // [1, 2, 3]
  • 68. @JosePaumard#Devoxx #J8Stream GroupingSpliterator Are we done with the underlying Stream? Stream.Builder<E> builder = Stream.builder() ; for (int i = 0 ; i < grouping ; i++) { spliterator.tryAdvance( element -> builder.add(element) ); } Stream<E> subStream = subBuilder.build(); // [1, 2, 3]
  • 69. @JosePaumard#Devoxx #J8Stream GroupingSpliterator Are we done with the underlying Stream? Stream.Builder<E> builder = Stream.builder() ; for (int i = 0 ; i < grouping ; i++) { spliterator.tryAdvance( // when this call returns false element -> builder.add(element) ); } Stream<E> subStream = subBuilder.build(); // [1, 2, 3]
  • 70. @JosePaumard#Devoxx #J8Stream GroupingSpliterator Are we done with the underlying Stream? Stream.Builder<E> builder = Stream.builder() ; boolean finished = false; for (int i = 0 ; i < grouping ; i++) { if (spliterator.tryAdvance(element -> builder.add(element))) finished = true; } Stream<E> subStream = subBuilder.build(); // [1, 2, 3]
  • 71. @JosePaumard#Devoxx #J8Stream GroupingSpliterator Are we done with the underlying Stream? public boolean tryAdvance(Consumer<? super Stream<E>> action) { Stream.Builder<E> builder = Stream.builder() ; boolean finished = false; for (int i = 0 ; i < grouping ; i++) { if (spliterator.tryAdvance(element -> builder.add(element))) finished = true; } Stream<E> subStream = subBuilder.build(); // [1, 2, 3] action.accept(subStream) ; return !finished ; }
  • 72. @JosePaumard#Devoxx #J8Stream RollingSpliterator What about building a Stream like this one: [[1, 2, 3], [2, 3, 4], [3, 4, 5], …] This time we need a ring buffer
  • 73. @JosePaumard#Devoxx #J8Stream RollingSpliterator The tryAdvance() call on the underlying Stream becomes this: bufferWriteIndex is an AtomicLong private boolean advanceSpliterator() { return spliterator.tryAdvance( element -> { buffer[bufferWriteIndex.get() % buffer.length] = element ; bufferWriteIndex.incrementAndGet() ; }); }
  • 74. @JosePaumard#Devoxx #J8Stream RollingSpliterator Building the element streams from the ring buffer: private Stream<E> buildSubstream() { Stream.Builder<E> subBuilder = Stream.builder() ; for (int i = 0 ; i < grouping ; i++) { subBuilder.add( (E)buffer[(i + bufferReadIndex.get()) % buffer.length] ) ; } bufferReadIndex.incrementAndGet() ; Stream<E> subStream = subBuilder.build() ; return subStream ; }
  • 75. @JosePaumard#Devoxx #J8Stream RollingSpliterator Putting things together to build the tryAdvance() call
  • 76. @JosePaumard#Devoxx #J8Stream public boolean tryAdvance(Consumer<? super Stream<E>> action) { boolean finished = false ; if (bufferWriteIndex.get() == bufferReadIndex.get()) { for (int i = 0 ; i < grouping ; i++) { if (!advanceSpliterator()) { finished = true ; } } } if (!advanceSpliterator()) { finished = true ; } Stream<E> subStream = buildSubstream() ; action.accept(subStream) ; return !finished ; }
  • 77. @JosePaumard#Devoxx #J8Stream Spliterator on Spliterator We saw how to build a Stream on another Stream by rearranging its elements What about building a Stream by merging the elements of other Streams?
  • 78. @JosePaumard#Devoxx #J8Stream Spliterator on Spliterators Let us take two Streams: [1, 2, 3, …] [a, b, c, …] And build a ZippingSpliterator: [F[1, a], F[2, b], F[3, c], …]
  • 79. @JosePaumard#Devoxx #J8Stream Spliterator on Spliterators What about estimateSize() trySplit() characteristics()? They are the same as the underlying streams
  • 80. @JosePaumard#Devoxx #J8Stream Spliterator on Spliterators We then need to implement tryAdvance() Where transform is a BiFunction public boolean tryAdvance(Consumer<? super R> action) { return spliterator1.tryAdvance( e1 -> { spliterator2.tryAdvance(e2 -> { action.accept(tranform.apply(e1, e2)) ; }) ; }) ; }
  • 81. @JosePaumard#Devoxx #J8Stream ZippingSpliterator What about creating a Builder for this Spliterator? ZippingSpliterator.Builder<String, String, String> builder = new ZippingSpliterator.Builder(); ZippingSpliterator<String,String,String> zippingSpliterator = builder .with(spliterator1) .and(spliterator2) .mergedBy((e1, e2) -> e1 + " - " + e2) .build();
  • 82. @JosePaumard#Devoxx #J8Stream ZippingSpliterator The complete pattern: Stream<String> stream1 = Stream.of("one", "two", "three"); Stream<Integer> stream2 = Stream.of(1, 2, 3); Spliterator<String> spliterator1 = stream1.spliterator(); Spliterator<Integer> spliterator2 = stream2.spliterator(); Stream<String> zipped = StreamSupport.stream(zippingSpliterator, false);
  • 83. @JosePaumard#Devoxx #J8Stream What did we do with Streams so far? We took one stream and built a stream by regrouping its elements in some ways
  • 84. @JosePaumard#Devoxx #J8Stream What did we do with Streams so far? We took one stream and built a stream by regrouping its elements in some ways We took to streams and we merged them, element by element, using a bifunction
  • 85. @JosePaumard#Devoxx #J8Stream What did we do with Streams so far? We took one stream and built a stream by regrouping its elements in some ways We took to streams and we merged them, element by element, using a bifunction What about taking one element at a time, from different streams?
  • 86. @JosePaumard#Devoxx #J8Stream The WeavingSpliterator We have N streams, and we want to build a stream that takes: - its 1st element from the 1st stream - its 2nd element from the 2nd stream, etc…
  • 87. @JosePaumard#Devoxx #J8Stream The WeavingSpliterator The estimateSize(): public long estimateSize() { int size = 0 ; for (Spliterator<E> spliterator : this.spliterators) { size += spliterator.estimateSize() ; } return size ; }
  • 88. @JosePaumard#Devoxx #J8Stream The WeavingSpliterator The tryAdvance(): private AtomicInteger whichOne = new AtomicInteger(); public boolean tryAdvance(Consumer<? super E> action) { return spliterators[whichOne.getAndIncrement() % spliterators.length] .tryAdvance(action); }
  • 89. @JosePaumard#Devoxx #J8Stream One last thing about Spliterators The characteristics() method is in fact quite important What does « characteristics » mean for a Spliterator?
  • 90. @JosePaumard#Devoxx #J8Stream The Spliterator It is in fact a special word: characteristics public interface Spliterator<T> { public static final int ORDERED = 0x00000010; public static final int DISTINCT = 0x00000001; public static final int SORTED = 0x00000004; public static final int SIZED = 0x00000040; public static final int NONNULL = 0x00000100; public static final int IMMUTABLE = 0x00000400; public static final int CONCURRENT = 0x00001000; public static final int SUBSIZED = 0x00004000; }
  • 91. @JosePaumard#Devoxx #J8Stream The Spliterator The Spliterator holds a special word: characteristics // ArrayListSpliterator public int characteristics() { return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; } // HashMap.KeySpliterator public int characteristics() { return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) | Spliterator.DISTINCT; }
  • 92. @JosePaumard#Devoxx #J8Stream The Spliterator The Spliterator holds a special word: characteristics This word is used for optimization people.stream() .sorted() // quicksort? .collect(Collectors.toList());
  • 93. @JosePaumard#Devoxx #J8Stream The Spliterator The Spliterator holds a special word: characteristics This word is used for optimization people.stream() .sorted() // quicksort? It depends on SORTED == 0 .collect(Collectors.toList());
  • 94. @JosePaumard#Devoxx #J8Stream The Spliterator The Spliterator holds a special word: characteristics This word is used for optimization SortedSet<Person> people = ...; people.stream() .sorted() // SORTED == 1, no quicksort .collect(Collectors.toList());
  • 95. @JosePaumard#Devoxx #J8Stream The Spliterator The Spliterator holds a special word: characteristics This word is used for optimization ArrayList<Person> people = ...; people.stream() .sorted() // SORTED == 0, quicksort .collect(Collectors.toList());
  • 96. @JosePaumard#Devoxx #J8Stream The characteristics can change Each Stream object in a pipeline has its own characteristics
  • 97. @JosePaumard#Devoxx #J8Stream The characteristics can change Each Stream object in a pipeline has its own characteristics Method Set to 0 Set to 1 filter() SIZED - map() DISTINCT, SORTED - flatMap() DISTINCT, SORTED, SIZED - sorted() - SORTED, ORDERED distinct() - DISTINCT limit() SIZED - peek() - - unordered() ORDERED -
  • 98. @JosePaumard#Devoxx #J8Stream The characteristics can change Each Stream object in a pipeline has its own characteristics Method Set to 0 Set to 1 filter() SIZED - map() DISTINCT, SORTED - flatMap() DISTINCT, SORTED, SIZED - sorted() - SORTED, ORDERED distinct() - DISTINCT limit() SIZED - peek() - - unordered() ORDERED -
  • 99. @JosePaumard#Devoxx #J8Stream The characteristics can change Each Stream object in a pipeline has its own characteristics Method Set to 0 Set to 1 filter() SIZED - map() DISTINCT, SORTED - flatMap() DISTINCT, SORTED, SIZED - sorted() - SORTED, ORDERED distinct() - DISTINCT limit() SIZED - peek() - - unordered() ORDERED -
  • 100. @JosePaumard#Devoxx #J8Stream Back to the Spliterator itself What did we do with the spliterator, and with the Stream built on it?
  • 101. @JosePaumard#Devoxx #J8Stream What did we do with Streams so far? We took one stream and built a stream by regrouping its elements in some ways We took to streams and we merged them, element by element, using a bifunction We took a collection of streams and built a stream by taking elements from them, in a given order
  • 102. @JosePaumard#Devoxx #J8Stream Wrap-up for the Stream API The Java 8 Stream API is not just about implementing map / filter / reduce or building hashmaps We can use it to manipulate data in advanced ways: - by deciding on what source we want to connect - by deciding how we can consume the data from that source
  • 104. @JosePaumard#Devoxx #J8Stream RxJava Open source API, available on Github Developed by Netflix RxJava is the Java version of ReactiveX .NET Python, Kotlin, JavaScript, Scala, Ruby, Groovy, Rust Android https://github.com/ReactiveX/RxJava
  • 105. @JosePaumard#Devoxx #J8Stream RxJava RxJava is not an alternative implementation of Java 8 Streams, nor the Collection framework It is an implementation of the Reactor pattern
  • 106. @JosePaumard#Devoxx #J8Stream RxJava The central class is the Observable class
  • 107. @JosePaumard#Devoxx #J8Stream RxJava The central class is the Observable class It’s big: ~10k lines of code
  • 108. @JosePaumard#Devoxx #J8Stream RxJava The central class is the Observable class It’s big: ~10k lines of code It’s complex: ~100 static methods, ~150 non-static methods
  • 109. @JosePaumard#Devoxx #J8Stream RxJava The central class is the Observable class It’s big: ~10k lines of code It’s complex: ~100 static methods, ~150 non-static methods It’s complex: not only because there is a lot of things in it, but also because the concepts are complex
  • 111. @JosePaumard#Devoxx #J8Stream RxJava Interface Observer used to « observe » an observable Interface Subscription used to model the link between an observer and an observable
  • 112. @JosePaumard#Devoxx #J8Stream Interface Observer A simple interface public interface Observer<T> { public void onNext(T t); public void onCompleted(); public void onError(Throwable e); }
  • 113. @JosePaumard#Devoxx #J8Stream How to subscribe Subscribing to an observable Observable<T> observable = ... ; Subscription subscription = observable.subscribe(observer) ;
  • 114. @JosePaumard#Devoxx #J8Stream How to subscribe Subscribing to an observable Observable<T> observable = ... ; Subscription subscription = observable.subscribe(observer) ; public interface Subscription { public void unsubscribe(); public void isUnsubscribe(); }
  • 115. @JosePaumard#Devoxx #J8Stream How to subscribe We can also just declare a callback Observable<T> observable = ... ; Observable<T> next = observable.doOnNext(System.out::println) ; Observable<T> error = observable.doOnError(System.out::println) ; Observable<T> each = observable.doOnEach(System.out::println) ;
  • 116. @JosePaumard#Devoxx #J8Stream RxJava – agenda Patterns to create Observables How to merge observables together Hot observables / backpressure
  • 117. @JosePaumard#Devoxx #J8Stream How to create an Observable
  • 118. @JosePaumard#Devoxx #J8Stream The usual ways Observable from collections and arrays Observable<String> obs1 = Observable.just("one", "two", "three") ; List<String> strings = Arrays.asList("one", "two", "three") ; Observable<String> obs2 = Observable.from(strings) ;
  • 119. @JosePaumard#Devoxx #J8Stream The usual ways Observable useful for tests never(): never emits anything Observable<String> empty = Observable.empty() ; Observable<String> never = Observable.never() ; Observable<String> error = Observable.<String>error(exception) ;
  • 120. @JosePaumard#Devoxx #J8Stream The usual ways Series and time series Observable<Long> longs = Observable.range(1L, 100L) ; // interval Observable<Long> timeSerie1 = Observable.interval(1L, TimeUnit.MILLISECONDS) ; // serie of longs // initial delay, then interval Observable<Long> timeSerie2 = Observable.timer(10L, 1L, TimeUnit.MILLISECONDS) ; // one 0
  • 121. @JosePaumard#Devoxx #J8Stream The usual ways The using() method 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 ) { }
  • 122. @JosePaumard#Devoxx #J8Stream How using() works The using() method 1) Creates a resource that will provide the elements 2) Uses this resource to create the elements 3) Applies the 3rd argument to the resource This 3rd element is in fact there to close the resource
  • 123. @JosePaumard#Devoxx #J8Stream How using() works Suppose we want to create an observable on the lines of a text file 1) We need a BufferedReader 2) The we need to wrap the lines one by one in Observables 3) Then we need to close the BufferedReader
  • 124. @JosePaumard#Devoxx #J8Stream How using() works Let us do that: Func0<BufferedReader> resourceFactory = () -> new BufferedReader(new FileReader(new File(fileName))); Action1<BufferedReader> disposeAction = br -> br.close();
  • 125. @JosePaumard#Devoxx #J8Stream How using() works The problem is that… we need to handle exceptions  Func0<BufferedReader> resourceFactory = () -> { try { return new BufferedReader( new FileReader(new File(fileName))); } catch (Exception e) { e.printStackTrace(); } return null; };
  • 126. @JosePaumard#Devoxx #J8Stream How using() works How can we create one Observable by file line?
  • 127. @JosePaumard#Devoxx #J8Stream How using() works How can we create one Observable by file line? We could read the file line by line, put the lines in a Collection, then build an Observable on that collection
  • 128. @JosePaumard#Devoxx #J8Stream How using() works How can we create one Observable by file line? We could read the file line by line, put the lines in a Collection, then build an Observable on that collection
  • 129. @JosePaumard#Devoxx #J8Stream How using() works How can we create one Observable by file line? We could read the file line by line, put the lines in a Iterator, then build an Observable on that iterator
  • 130. @JosePaumard#Devoxx #J8Stream How using() works How can we create one Observable by file line? We could read the file line by line, put the lines in a Iterator, then build an Observable on that iterator Creating an Observable from an Iterator: Observable.from(() -> iterator);
  • 131. @JosePaumard#Devoxx #J8Stream Func1<BufferedReader, String> observableFactory = bufferedReader -> { Iterator<String> readerIterator = new Iterator<String>() { private String currentLine = null; private boolean finished; { currentLine = bufferedReader.readLine(); finished = currentLine == null; } public boolean hasNext() { return !finished; } public String next() { String line = currentLine; currentLine = bufferedReader.readLine(); finished = currentLine == null; return line; } }; return Observable.from(() -> readerIterator); }
  • 132. @JosePaumard#Devoxx #J8Stream How using() works The using() method Made to build an Observable on a resource that needs to be « closed » at the end of the generation of the stream
  • 133. @JosePaumard#Devoxx #J8Stream How using() works The using() method Made to build an Observable on a resource that needs to be « closed » at the end of the generation of the stream It is not built on the auto closeable mechanism because RxJava aims to be Java 6 compatible
  • 135. @JosePaumard#Devoxx #J8Stream RxJava & executors Some of those methods take a further argument This Observable is executed in this scheduler Scheduler is an interface (in fact an abstract class, to define « default methods » in Java 7) Schedulers should be used to create schedulers Observable<Long> longs = Observable.range(0L, 100L, scheduler) ;
  • 136. @JosePaumard#Devoxx #J8Stream RxJava & executors It allows for the execution of Observable in specialized pools of Threads Some Observables are executed in special schedulers (cf Javadoc)
  • 137. @JosePaumard#Devoxx #J8Stream Schedulers Factory Schedulers public final class Schedulers { public static Scheduler immediate() {...} // immediate public static Scheduler newThread() {...} // new thread public static Scheduler trampoline() {...} // queued in the current // thread }
  • 138. @JosePaumard#Devoxx #J8Stream Schedulers Factory Schedulers public final class Schedulers { public static Scheduler computation() {...} // computation ES public static Scheduler io() {...} // IO growing ES public static Scheduler test() {...} public static Scheduler from(Executor executor) {...} }
  • 139. @JosePaumard#Devoxx #J8Stream Schedulers Schedulers can be seen as Executors Some of them are specialized (IO, Computation) Observers are called in the thread that runs the Observable
  • 140. @JosePaumard#Devoxx #J8Stream Schedulers The Schedulers.from(executor) is useful to call observers in certain threads For instance: Scheduler swingScheduler = Schedulers.from( SwingUtilities::invokeLater );
  • 141. @JosePaumard#Devoxx #J8Stream A 1st example A simple example Observable<Integer> range1To100 = Observable.range(1L, 100L) ; range1To100.subscribe(System.out::println) ;
  • 142. @JosePaumard#Devoxx #J8Stream A 1st example A simple example Observable<Integer> range1To100 = Observable.range(1L, 100L) ; range1To100.subscribe(System.out::println) ; > 1 2 3 4 ... 100
  • 143. @JosePaumard#Devoxx #J8Stream A 2nd example A not so simple example Observable<Integer> timer = Observable.timer(1, TimeUnit.SECONDS) ; timer.subscribe(System.out::println) ;
  • 144. @JosePaumard#Devoxx #J8Stream A 2nd example A not so simple example Nothing is printed Observable<Integer> timer = Observable.timer(1, TimeUnit.SECONDS) ; timer.subscribe(System.out::println) ; >
  • 145. @JosePaumard#Devoxx #J8Stream A 2nd example Let us modify this code Observable<Integer> timer = Observable.timer(1, TimeUnit.SECONDS) ; timer.subscribe(() -> { System.out.println(Thread.currentThread().getName() + " " + Thread.currentThread().isDaemon()) ; }) ; Thread.sleep(2) ;
  • 146. @JosePaumard#Devoxx #J8Stream A 2nd example Let us modify this code 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
  • 147. @JosePaumard#Devoxx #J8Stream About the 1st & 2nd examples The first example ran in the main thread
  • 148. @JosePaumard#Devoxx #J8Stream About the 1st & 2nd examples The first example ran in the main thread The second example ran in a daemon thread, that does not prevent the JVM from exiting. Nothing was printed out, because it did not have the time to be executed.
  • 149. @JosePaumard#Devoxx #J8Stream About the 1st & 2nd examples The first example ran in the main thread The second example ran in a daemon thread, that does not prevent the JVM from exiting. Nothing was printed out, because it did not have the time to be executed. Observable are ran in their own schedulers (executors)
  • 151. @JosePaumard#Devoxx #J8Stream Merging Observable together RxJava provides a collection of methods to merge observables together
  • 152. @JosePaumard#Devoxx #J8Stream Merging Observable together RxJava provides a collection of methods to merge observables together With many different, very interesting semantics
  • 153. @JosePaumard#Devoxx #J8Stream Representing an Observable RxJava uses « marble diagrams » to represent observables Observable
  • 154. @JosePaumard#Devoxx #J8Stream Representing an Observable RxJava uses « marble diagrams » to represent observables On this example, onNext() is called 5 times Observable
  • 155. @JosePaumard#Devoxx #J8Stream Representing an Observable RxJava uses « marble diagrams » to represent observables Here an exception has been thrown, no data will be generated after it, the onError() method is called Observable
  • 156. @JosePaumard#Devoxx #J8Stream Representing an Observable RxJava uses « marble diagrams » to represent observables Here the end of the Stream has been reached, the onComplete() method is called Observable
  • 157. @JosePaumard#Devoxx #J8Stream The ambiguous operator The first operator is amb(), that stands for « ambiguous » It takes the first Observable that produced data ©RxJava O1 O2 result
  • 158. @JosePaumard#Devoxx #J8Stream The zip operator The zip operator takes one element from each Observable and combine them using a function ©RxJava O1 O2 F(O1, O2)
  • 159. @JosePaumard#Devoxx #J8Stream The combine latest operator The combineLatest is a non-strict zip operator, emits an item each time an observable emits one ©RxJava O1 O2 F(O1, O2)
  • 160. @JosePaumard#Devoxx #J8Stream Concat and merge Concat: emits O1 and then O2, without mixing them, merge stops on the emission of an error ©RxJava©RxJava
  • 161. @JosePaumard#Devoxx #J8Stream Observables of Observables The methods we saw are defined on Iterables of Observables public final static <T> Observable<T> merge( Iterable<Observable<T>> listOfSequences) { }
  • 162. @JosePaumard#Devoxx #J8Stream Observables of Observables The methods we saw are defined on Iterables of Observables They are also defined on Observables of Observables public final static <T> Observable<T> merge( Iterable<Observable<T>> listOfSequences) { } public final static <T> Observable<T> merge( Observable<Observable<T>> sequenceOfSequences) { }
  • 163. @JosePaumard#Devoxx #J8Stream Observables of Observables And the marble diagrams are different public final static <T> Observable<T> merge( Iterable<Observable<T>> listOfSequences) { } ©RxJava
  • 164. @JosePaumard#Devoxx #J8Stream Observables of Observables And the marble diagrams are different public final static <T> Observable<T> merge( Observable<Observable<T>> sequenceOfSequences) { } ©RxJava
  • 165. @JosePaumard#Devoxx #J8Stream Observables of Observables Then comes the switchOnNext operator public final static <T> Observable<T> switchOnNext( Observable<Observable<T>> sequenceOfSequences) { } ©RxJava
  • 167. @JosePaumard#Devoxx #J8Stream Cold and hot Observable A cold Observable emits items if it is observered
  • 168. @JosePaumard#Devoxx #J8Stream Cold and hot Observable A cold Observable emits items if it is observered A hot Observable emits items when it is created
  • 169. @JosePaumard#Devoxx #J8Stream A 3rd example Let us see an example Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS); Observable<String> manyStrings = Observable.combineLatest( timer, Observable.just("one"), (i, s) -> i + " - " + s).forEach(System.out::println);
  • 170. @JosePaumard#Devoxx #J8Stream A 3rd example Let us see an example Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS); Observable<String> manyStrings = Observable.combineLatest( timer, Observable.just("one"), (i, s) -> i + " - " + s).forEach(System.out::println); > 0 - one 1 - one 2 – one ...
  • 171. @JosePaumard#Devoxx #J8Stream A 3rd example Let us see an example Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS); Thread.sleep(5500); Observable<String> manyStrings = Observable.combineLatest( timer, Observable.just("one"), (i, s) -> i + " - " + s).forEach(System.out::println);
  • 172. @JosePaumard#Devoxx #J8Stream A 3rd example Let us see an example Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS); Thread.sleep(5500); Observable<String> manyStrings = Observable.combineLatest( timer, Observable.just("one"), (i, s) -> i + " - " + s).forEach(System.out::println); > 0 - one 1 - one 2 – one ...
  • 173. @JosePaumard#Devoxx #J8Stream A 3rd example It means that the timer does not increment itself if not observed This Observable is cold
  • 174. @JosePaumard#Devoxx #J8Stream A 3rd example It means that the timer does not increment itself if not observed This Observable is cold How can we make it hot?
  • 175. @JosePaumard#Devoxx #J8Stream A 3rd example Making it hot: Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS); Observable<String> manyStrings = Observable.combineLatest( timer, Observable.just("one"), (i, s) -> i + " - " + s).forEach(System.out::println); Observable<String> moreManyStrings = Observable.combineLatest( timer, Observable.just("two"), (i, s) -> i + " - " + s).forEach(System.out::println);
  • 176. @JosePaumard#Devoxx #J8Stream A 3rd example It prints out the following: > 0 - one 0 - two 1 – one 1 - two 2 – one 2 – two ...
  • 177. @JosePaumard#Devoxx #J8Stream A 3rd example Making it hot:Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS); Observable<String> manyStrings = Observable.combineLatest( timer, Observable.just("one"), (i, s) -> i + " - " + s).forEach(System.out::println); Thread.sleep(5500); Observable<String> moreManyStrings = Observable.combineLatest( timer, Observable.just("two"), (i, s) -> i + " - " + s).forEach(System.out::println);
  • 178. @JosePaumard#Devoxx #J8Stream A 3rd example It then prints out the following! > 0 - one 1 - one 2 - one 3 - one 4 - one 5 - one 5 - two 6 - one 6 - two 7 - one 7 – two ...
  • 179. @JosePaumard#Devoxx #J8Stream Cold and hot Observable So in its first use, timer is a cold Observable And in the second one, it becomes a hot Observable If used more than once, a cold Observable will be seen hot by the 2nd and later observers
  • 180. @JosePaumard#Devoxx #J8Stream Cold and hot Observable Problem: what happens if we have many observers to subscribe?
  • 181. @JosePaumard#Devoxx #J8Stream Cold and hot Observable Problem: what happens if we have many observers to subscribe? Our application could miss the first values emitted by an observable How can we fix that?
  • 182. @JosePaumard#Devoxx #J8Stream Cold and hot Observable We can use the cache method cache(int capacity) { } O1 O2
  • 183. @JosePaumard#Devoxx #J8Stream A cached timer Creating a cached timer: Observable<Long> timer = Observable.interval(1, TimeUnit.SECONDS); Observable<Long> cachedTimer = timer.cache(1_000); ...
  • 184. @JosePaumard#Devoxx #J8Stream ConnectableObservable We can do better and use a ConnectableObservable (which is an Observable) // does not count as a subscription ConnectableObservable<Long> publish = observable.publish(); // take the time to connect all the observers // then call publish.connect();
  • 185. @JosePaumard#Devoxx #J8Stream Cold and hot Observable In fact the Observable returned by the connect() call is itself a hot Observable So this is also a way to create a hot Observable
  • 187. Coffe break!Map, filter, reduce, collect Repeating & retrying Dealing with time Backpressure Combining Java 8 Streams & RxJava Comparisons The future: reactive support in Java 9
  • 189. @JosePaumard#Devoxx #J8Stream Map and filter Map / filter, return an Observable Emits only the elements that match the predicate map(Func1<T, R> mapping) { } cast(Class<R> clazz) { } // casts the elements filter(Func1<T, Boolean> predicate) { }
  • 190. @JosePaumard#Devoxx #J8Stream Materializing Materialize: special type of mapping Emit a Notification object that wraps the item, with metadata Useful for logging Does the reverse: materialize() { } // Observable<Notification<T>> dematerialize() { } // Observable<T>
  • 191. @JosePaumard#Devoxx #J8Stream Selecting ranges Selecting ranges of elements first() { } last() { } skip(int n) { } limit(int n) { } take(int n) { }
  • 192. @JosePaumard#Devoxx #J8Stream Special filtering Special functions Emits a true then complete on the onComplete if the Observable emitted at least one item exists(Func1<T, Boolean> predicate) { }
  • 193. @JosePaumard#Devoxx #J8Stream Special filtering Special functions Emits the nth item then complete on the onNext if the Observable emitted at least n items elementAt(int n) { }
  • 194. @JosePaumard#Devoxx #J8Stream Special filtering Special functions Emits nothing then complete on the onComplete ignoreElement() { }
  • 195. @JosePaumard#Devoxx #J8Stream Special filtering Special functions Emits the first item on the onComplete or emits an error on the second item emitted single() { }
  • 196. @JosePaumard#Devoxx #J8Stream Special filtering Special functions Emits the matching item if it has been seen, on onComplete or emits an error if not, on onComplete single(Func1<T, Boolean> predicate) { }
  • 197. @JosePaumard#Devoxx #J8Stream Reductions & collections Classical reductions all(Func1<T, Boolean> predicate) { } // Observable<Boolean> count() { } // Observable<Long> forEach(Action1<T> consumer) { } // void
  • 198. @JosePaumard#Devoxx #J8Stream Reductions & collections Accumulations Reduce: returns the reduction of all the items, on onComplete Returns an error if the observable is empty reduce(Func2<T, T, T> accumulator) { } // Observable<T>
  • 199. @JosePaumard#Devoxx #J8Stream Reductions & collections Accumulations Reduce: returns the reduction of all the items, on onComplete In case the observable might be empty, use the other version of reduce, that takes a seed reduce(Func2<T, T, T> accumulator) { } // Observable<T> reduce(seed, Func2<T, T, T> accumulator) { } // Observable<T>
  • 200. @JosePaumard#Devoxx #J8Stream Reductions & collections Accumulations Scan: returns the reduction step by step, on onNext Can also take a seed reduce(Func2<T, T, T> accumulator) { } // Observable<T> scan(Func2<T, T, T> accumulator) { } // Observable<T> reduce(seed, Func2<T, T, T> accumulator) { } // Observable<T>
  • 201. @JosePaumard#Devoxx #J8Stream Reductions & collections Collecting data in a mutable container Example: adding the elements to a list collect(Func0<R> stateFactory, // Producer Action2<R, T> collector) { } // BiConsumer collect(ArrayList::new, // Producer ArrayList::add) { } // BiConsumer
  • 202. @JosePaumard#Devoxx #J8Stream Reductions & collections Ready to use collectors for lists: toList() { } // Observable<List<T>> toSortedList() { } // Observable<List<T>>
  • 203. @JosePaumard#Devoxx #J8Stream Reductions & collections Ready to use collectors for lists: And for maps (toMap can lose items): toList() { } // Observable<List<T>> toSortedList() { } // Observable<List<T>> toMultimap(Func1<T, K> keySelector, // Observable< Func1<T, V> valueSelector) { } // Map<K, Collection<T>> toMap(Func1<T, K> keySelector) { } // Observable<Map<K, T>>
  • 204. @JosePaumard#Devoxx #J8Stream Reductions & collections A special kind of map: The returned observable could hold a single map, but it does not groupBy(Func1<T, K> keySelector) { } // function
  • 205. @JosePaumard#Devoxx #J8Stream Reductions & collections A special kind of map: The returned observable could hold a single map, but it does not It holds GroupedObservable items, which is an Observable with a key groupBy(Func1<T, K> keySelector) { } // function
  • 206. @JosePaumard#Devoxx #J8Stream Reductions & collections A special kind of map: The returned observable could hold a single map, but it does not Instead of returning an Iterable of key / value pairs It returns an Observable of key / value pairs groupBy(Func1<T, K> keySelector) { } // function
  • 207. @JosePaumard#Devoxx #J8Stream Reductions & collections Let us see an example Observable<Person> people = ...; // Observable<GroupedObservable<Integer, Person>> people.groupBy(Person::getAge)
  • 208. @JosePaumard#Devoxx #J8Stream Reductions & collections Let us see an example Observable<Person> people = ...; // Observable<GroupedObservable<Integer, Person>> people.groupBy(Person::getAge) .filter(groupedObs -> groupedObs.getKey() > 20)
  • 209. @JosePaumard#Devoxx #J8Stream Reductions & collections Let us see an example Observable<Person> people = ...; // GroupedObservable<Integer, Person> extends Observable<Person> people.groupBy(Person::getAge) .filter(groupedObs -> groupedObs.getKey() > 20) .flatMap(groupedObs -> groupedObs)
  • 210. @JosePaumard#Devoxx #J8Stream Reductions & collections Let us see an example Observable<Person> people = ...; // Observable<Integer> people.groupBy(Person::getAge) .filter(groupedObs -> groupedObs.getKey() > 20) .flatMap(groupedObs -> groupedObs) .count();
  • 211. @JosePaumard#Devoxx #J8Stream Wrap-up on map / filter / reduce All those methods return an Observable The result is thus wrapped in an Observable Chaining operations is made with flatMap() Wrapping and unwrapping has a cost…
  • 213. @JosePaumard#Devoxx #J8Stream Repeating & retrying The repeat method repeats the same Observable repeat() { } // Observable<T> repeat(long times) { } // Observable<T>
  • 214. @JosePaumard#Devoxx #J8Stream Repeating & retrying The repeat method repeats the same Observable repeat() { } // Observable<T> repeat(long times) { } // Observable<T> repeatWhen( Func1<Observable<Void>>, Observable<?>> notificationHandler ) { }
  • 215. @JosePaumard#Devoxx #J8Stream Repeating & retrying repeat: will repeat the same Observable again and again repeatWhen: - on the onComplete of the source Observable, the notification handler is passed an Observable - if the notification handler calls the onError or onComplete of this passed Observable, then repeatWhen calls the same on the returned Observable
  • 216. @JosePaumard#Devoxx #J8Stream Repeating & retrying repeat: will repeat the same Observable again and again On the returned Observable, one can invoke: onNext(), that triggers the repetition onComplete() or onError(), which will trigger the same call on the source Observable
  • 217. @JosePaumard#Devoxx #J8Stream Repeating & retrying This function takes an Observable<Void> And returns an Observable<?>
  • 218. @JosePaumard#Devoxx #J8Stream Repeating & retrying This function takes an Observable<Void> And returns an Observable<?> When the Observable to be repeated calls onComplete Then the passed Observable calls onNext with null As a response the returned Observable should call: - onNext to repeat - onComplete to stop repeating
  • 219. @JosePaumard#Devoxx #J8Stream Repeating & retrying The first thing we need is an Observable on which we can emit items on demand
  • 220. @JosePaumard#Devoxx #J8Stream Repeating & retrying The first thing we need is an Observable on which we can emit items on demand For that, we need to implement the OnSubscribe interface final Set<Subscriber<? super Void>> subscribers = new HashSet<>(); OnSubscribe<Void> onSubscribe = new OnSubscribe<Void>() { public void call(Subscriber<? super Void> subscriber) { subscribers.add(subscriber); } };
  • 221. @JosePaumard#Devoxx #J8Stream Repeating & retrying Then create an Observable from this onSubscribe object final Observable<Void> returnedObservable = Observable.create(onSubscribe); final Set<Subscriber<? super Void>> subscribers = new HashSet<>(); OnSubscribe<Void> onSubscribe = new OnSubscribe<Void>() { public void call(Subscriber<? super Void> subscriber) { subscribers.add(subscriber); } };
  • 222. @JosePaumard#Devoxx #J8Stream Repeating & retrying Then create the notification handler Func1 notificationHandler = new Func1<Observable<Void>, Observable<Void>>() { final Observable<Void> returnedObservable = ...; public Observable<Void> call(Observable<Void> observable) { // implementation } return returnedObservable; };
  • 223. @JosePaumard#Devoxx #J8Stream Repeating & retrying Then create the notification handler observable.subscribe(new Observer<Void>() { private int count = 0; public void onCompleted() { } public void onError(Throwable e) { subscribers.forEach(sub -> sub.onError(e)); }
  • 224. @JosePaumard#Devoxx #J8Stream Repeating & retrying Then create the notification handler int count = 0; public void onNext(Void t) { if (count == 3) { subscribers.forEach(sub -> sub.onCompleted()); // stops } else { count++; Thread.sleep(1_000); subscribers.forEach(sub -> sub.onNext(null)); // repeat } }
  • 225. @JosePaumard#Devoxx #J8Stream Repeating & retrying The complete pattern Observable<Long> timer = Observable.interval(1, TimeUnit.MILLISECONDS).take(5); timer.repeatWhen( (Func1<? super Observable<? extends Void>, ? extends Observable<?>>) notificationHandler) .forEach(System.out::println); Thread.sleep(5_000);
  • 226. @JosePaumard#Devoxx #J8Stream Repeating & retrying The retry method will reset the Observable on error The retryThen() function works in the same way as for the repeat retry() { } // Observable<T> retry(long times) { } // Observable<T> retryWhen( Func1<Observable<Throwable>>, Observable<?>> notificationHandler ) { }
  • 227. @JosePaumard#Devoxx #J8Stream Repeating & retrying There is also a retry family of methods, that works on onError instead of onComplete
  • 228. @JosePaumard#Devoxx #J8Stream Joining Joins to Observable, based on the overlapping of durations join(Observable<TRight> right, Func1<T, Observable<TLeftDuration>> leftDurationSelector, Func1<TRight, Observable<TRightDuration>> rightDurationSelector, Func2<T, TRight, R> resultSelector) { }
  • 229. @JosePaumard#Devoxx #J8Stream Join Joins to Observable, based on the overlapping of durations ©RxJava Left obs. Right obs. F(O1, O2)
  • 230. @JosePaumard#Devoxx #J8Stream GroupJoin Joins to Observable, based on the overlapping of durations groupJoin( Observable<T2> right, Func1<T, Observable<D1>> leftDuration, Func1<T2, Observable<D2>> rightDuration, Func2<T, <T2>, R> resultSelector) { }
  • 231. @JosePaumard#Devoxx #J8Stream GroupJoin Joins to Observable, based on the overlapping of durations ©RxJava Left obs. Right obs. F(O1, O2)
  • 233. @JosePaumard#Devoxx #J8Stream Dealing with the time RxJava has a set of methods to deal with time Let us go back on our cold / hot Observables It is easy to imagine a hot Observable that emits an onNext event each second, thus playing the role of a clock
  • 234. @JosePaumard#Devoxx #J8Stream Sampling With hot observables, RxJava can synchronize on a clock sample(long period, TimeUnit timeUnit) { } ©RxJava
  • 235. @JosePaumard#Devoxx #J8Stream Sampling Or synchronize on a reference Observable! sample(Observable<U> sampler) { } // samples on emission & completion ©RxJava O1 sampler
  • 236. @JosePaumard#Devoxx #J8Stream RxJava and synchronization A very powerful feature: RxJava can synchronize on a clock (real-time) And on another Observable, it can then take its own time scale
  • 237. @JosePaumard#Devoxx #J8Stream Measuring time If we can measure time, then we can emit it Will emit an the amount of time between the two last onNext events timeInterval() { } // Observable<TimeInterval<T>>
  • 238. @JosePaumard#Devoxx #J8Stream Measuring time If we can measure time, then we can delay an emission Will reemit the items, with a delay This delay can be computed from the items themselves delay(long delay, TimeUnit timeUnit) ; // Observable<T> delay(Func1<T, Observable<U> func1) ; // Observable<T>
  • 239. @JosePaumard#Devoxx #J8Stream Measuring time If we can measure time, then we can timeout Will emit an error if no item is seen during this time timeout(long n, TimeUnit timeUnit) { }
  • 241. @JosePaumard#Devoxx #J8Stream Fast hot observables What happens if a hot Observable emits too many items? What happens when an Observer cannot keep up the pace of an Observable?
  • 242. @JosePaumard#Devoxx #J8Stream Fast hot observables What happens if a hot Observable emits too many items? What happens when an Observer cannot keep up the pace of an Observable? This leads to the notion of backpressure
  • 243. @JosePaumard#Devoxx #J8Stream Backpressure methods Backpressure is a way to slow down the emission of elements It can act on the observing side Several strategies: - Buffering items - Skipping items (sampling is a way to implement this)
  • 244. @JosePaumard#Devoxx #J8Stream Backpressure methods Use cases can be very different! 1) The temperature sensors example - Suppose our application handle a group of temperature sensors - Each sensor sends one measure every seconds
  • 245. @JosePaumard#Devoxx #J8Stream Backpressure methods Use cases can be very different! 1) The temperature sensors example - Suppose our application handle a group of temperature sensors - Each sensor sends one measure every seconds Maybe we can just keep one measure every hour! Loosing items ≠ loosing information!
  • 246. @JosePaumard#Devoxx #J8Stream Backpressure methods Use cases can be very different! 2) The YouTube example - YouTube sends a HD video to your browser - But my bandwidth is too low
  • 247. @JosePaumard#Devoxx #J8Stream Backpressure methods Use cases can be very different! 2) The YouTube example - YouTube sends a HD video to your browser - But my bandwidth is too low Instead of sending HD, YouTube will send me 720p
  • 248. @JosePaumard#Devoxx #J8Stream Backpressure methods Use cases can be very different! 2) The YouTube example - YouTube sends a HD video to your browser - But my bandwidth is too low Instead of sending HD, YouTube will send me 720p Loosing items ≠ loosing information!
  • 249. @JosePaumard#Devoxx #J8Stream Backpressure Here we are looking at the backpressure from the observer point of view But it can also clog a network Acting at the Observable level can be important too!
  • 250. @JosePaumard#Devoxx #J8Stream Buffering Buffering records data in a buffer and emits it buffer(int size) { } // Observable<List<T>> buffer(long timeSpan, TimeUnit unit) { } // Observable<List<T>> buffer(long timeSpan, TimeUnit unit, int maxSize) { }
  • 251. @JosePaumard#Devoxx #J8Stream Buffering Buffering records data in a buffer (a list) and emits it buffer(int size) { } // Observable<List<T>> buffer(long timeSpan, TimeUnit unit) { } // Observable<List<T>> buffer(long timeSpan, TimeUnit unit, int maxSize) { } buffer(Observable<O> bufferOpenings, // Openings events Func1<O, Observable<C>> bufferClosings) { } // Closings events
  • 252. @JosePaumard#Devoxx #J8Stream Windowing Windowing acts as a buffer, but emits Observables instead of lists of buffered items window(int size) { } // Observable<Observable<T>> window(long timeSpan, TimeUnit unit) { } // Observable<Observable<T>> window(long timeSpan, TimeUnit unit, int maxSize) { } window(Observable<O> bufferOpenings, // Openings events Func1<O, Observable<C>> bufferClosings) { } // Closings events
  • 253. @JosePaumard#Devoxx #J8Stream Buffering & windowing ©RxJava ©RxJava
  • 254. @JosePaumard#Devoxx #J8Stream Throttling Throttling is a sampler on the beginning or the end of a window throttleFirst(long windowDuration, TimeUnit unit) { } // Observable<T> throttleLast(long windowDuration, TimeUnit unit) { } throttleWithTimeout(long windowDuration, TimeUnit unit) { }
  • 255. @JosePaumard#Devoxx #J8Stream Throttling throttleFirst: emits the first item in a window of time throttleLast: emits the last item from that window, on the next tick of the clock
  • 256. @JosePaumard#Devoxx #J8Stream Throttling Throttling is a sampler on the beginning or the end of a window ©RxJava
  • 257. @JosePaumard#Devoxx #J8Stream Debouncing Debounce also limits the rate of the emission of the items, by adding a delay before reemitting debounce(long delay, TimeUnit timeUnit) ; // Observable<T> debounce(Func1<T, Observable<U> func1) ; // Observable<T>
  • 258. @JosePaumard#Devoxx #J8Stream Debouncing It two items are emitted within the same time frame, the first one is lost ©RxJava
  • 259. @JosePaumard#Devoxx #J8Stream Wrap-up on RxJava Complex API, many different concepts, many methods Allows to process data in chosen threads, this is useful for IO, computations, specialized threads (GUI threads) Allows the synchronization of operations on clocks on application events Works in pull mode, and also in push mode backpressure
  • 260. Getting the best of both worlds?
  • 261. @JosePaumard#Devoxx #J8Stream Getting the best of both API? What about connecting a J8 Stream to a Rx Observable?
  • 262. @JosePaumard#Devoxx #J8Stream Getting the best of both API? If we have an Iterator, this is easy: Iterator<T> iterator = ... ; Observable<T> observable = Observable.from(() -> iterator) ;
  • 263. @JosePaumard#Devoxx #J8Stream Getting the best of both API? If we have an Iterator, this is easy: If we have a Spliterator, not much more complex: Iterator<T> iterator = ... ; Observable<T> observable = Observable.from(() -> iterator) ; Spliterator<T> spliterator = ... ; Observable<T> observable = Observable.from(() -> Spliterators.iterator(spliterator)) ;
  • 264. @JosePaumard#Devoxx #J8Stream Getting the best of both API? So if we have a Stream, we can easily build an Observable
  • 265. @JosePaumard#Devoxx #J8Stream Getting the best of both API? So if we have a Stream, we can easily build an Observable What about the other way?
  • 266. @JosePaumard#Devoxx #J8Stream Getting the best of both API? So if we have a Stream, we can easily build an Observable What about the other way? 1) We can build an Iterator on an Observable 2) Then build a Spliterator on an Iterator
  • 267. @JosePaumard#Devoxx #J8Stream Getting the best of both API? So if we have a Stream, we can easily build an Observable What about the other way? 1) We can build an Iterator on an Observable 2) Then build a Spliterator on an Iterator But we need to do that ourselves…
  • 268. @JosePaumard#Devoxx #J8Stream Iterating on an Observable To implement an Iterator: 1) We need to implement next() and hasNext() 2) remove() is a default method in Java 8
  • 269. @JosePaumard#Devoxx #J8Stream Iterating on an Observable The trick is that an iterator pulls the date from a source an observable pushes the data to callbacks
  • 270. @JosePaumard#Devoxx #J8Stream Iterating on an Observable The trick is that an iterator pulls the date from a source an observable pushes the data to callbacks So we need an adapter…
  • 271. @JosePaumard#Devoxx #J8Stream Iterating on an Observable How has it been done in the JDK? public static<T> Iterator<T> iterator(Spliterator<? extends T> spliterator) { class Adapter implements Iterator<T>, Consumer<T> { // implementation } return new Adapter() ; }
  • 272. @JosePaumard#Devoxx #J8Stream 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 ; } } }
  • 273. @JosePaumard#Devoxx #J8Stream Iterating on an Observable Let us adapt this pattern! public static<T> Iterator<T> of(Observable<? extends T> observable) { class Adapter implements Iterator<T>, Consumer<T> { // implementation } return new Adapter() ; }
  • 274. @JosePaumard#Devoxx #J8Stream class Adapter implements Iterator<T>, Consumer<T> { boolean valueReady = false ; T nextElement; public void accept(T t) { // needs to get the data from the Observable valueReady = true ; nextElement = t ; } public boolean hasNext() { return valueReady ; } public T next() { if (!valueReady && !hasNext()) throw new NoSuchElementException() ; else { valueReady = false ; return nextElement ; } } }
  • 275. @JosePaumard#Devoxx #J8Stream Iterating on an Observable The accept method class 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 ) ; } }
  • 276. @JosePaumard#Devoxx #J8Stream Iterating on an Observable The accept method class 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 ) ; } } final…
  • 277. @JosePaumard#Devoxx #J8Stream Iterating on an Observable We can wrap those value in Atomic variable class 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 ) ; } }
  • 278. @JosePaumard#Devoxx #J8Stream Iterating on an Observable Cant we do better? interface Wrapper<E> { E get() ; } Wrapper<Boolean> wb = () -> true ;
  • 279. @JosePaumard#Devoxx #J8Stream Iterating on an Observable Cant we do better? interface Wrapper<E> { E get() ; } Wrapper<Boolean> wb = () -> true ; Action1<Boolean> onNext = b -> wb.set(b) ; // should return Wrapper<T>
  • 280. @JosePaumard#Devoxx #J8Stream Iterating on an Observable Cant we do better? interface Wrapper<E> { E get() ; public default Wrapper<E> set(E e) { // should return a wrapper of e } } Wrapper<Boolean> wb = () -> true ; Action1<Boolean> onNext = b -> wb.set(b) ; // should return Wrapper<T>
  • 281. @JosePaumard#Devoxx #J8Stream Iterating on an Observable Cant we do better? 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>
  • 282. @JosePaumard#Devoxx #J8Stream Iterating on an Observable We can wrap those value in Atomic variable class 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 ) ; } }
  • 283. @JosePaumard#Devoxx #J8Stream Iterating on an Observable So we can build an Iterator on an Observable And with it, a Spliterator on an Observable it will work on cold observables
  • 284. @JosePaumard#Devoxx #J8Stream Getting the best of both API The cold observables can be implemeted with Java 8 Streams The hot observables can be implemented by combining both API
  • 286. @JosePaumard#Devoxx #J8Stream Let us do some comparisons Let us take one use case Implemented in Rx and J8 Streams See the different ways of writing the same processings And compare the processing times using JMH
  • 287. @JosePaumard#Devoxx #J8Stream Shakespeare plays Scrabble Published in Java Magazine Presented in Devoxx 2014 Used during Virtual Technology Summit https://community.oracle.com/docs/DOC-916777 https://github.com/JosePaumard/jdk8-lambda-tour
  • 288. @JosePaumard#Devoxx #J8Stream Shakespeare plays Scrabble Basically, we have the set of the words used by Shakespeare The official Scrabble player dictionnary And the question is: how good at Scrabble Shakespeare would have been?
  • 289. @JosePaumard#Devoxx #J8Stream Shakespeare plays Scrabble 1) Build the histogram of the letters of a word 2) Number of blanks needed for a letter in a word 3) Number of blanks needed to write a word 4) Predicate to check is a word can be written with 2 blanks 5) Bonus for a doubled letter 6) Final score of a word 7) Histogram of the scores 8) Best word
  • 290. @JosePaumard#Devoxx #J8Stream 1) Histogram of the letters Java 8 Stream API – 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() ) ) ;
  • 291. @JosePaumard#Devoxx #J8Stream 1) Histogram of the letters Java 8 Stream API – 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()) ; }) ;
  • 292. @JosePaumard#Devoxx #J8Stream 1) Histogram of the letters Java 8 Stream API – 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() ; } }
  • 293. @JosePaumard#Devoxx #J8Stream 2) # of blanks for a letter Java 8 Stream API – 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'] ) ;
  • 294. @JosePaumard#Devoxx #J8Stream 2) # of blanks for a letter Java 8 Stream API – 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']
  • 295. @JosePaumard#Devoxx #J8Stream 3) # of blanks for a word Java 8 Stream API – Rx Java // number of blanks for a given word Function<String, Long> nBlanks = word -> histoOfLetters.apply(word) .entrySet().stream() .mapToLong(blank) .sum();
  • 296. @JosePaumard#Devoxx #J8Stream 3) # of blanks for a word Java 8 Stream API – 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) ;
  • 297. @JosePaumard#Devoxx #J8Stream 4) Predicate for 2 blanks Java 8 Stream API – 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? Func1<String, Observable<Boolean>> checkBlanks = word -> nBlanks.call(word) .flatMap(l -> Observable.just(l <= 2L)) ;
  • 298. @JosePaumard#Devoxx #J8Stream 5) Bonus for a doubled letter – 1 Java 8 Stream API – 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);
  • 299. @JosePaumard#Devoxx #J8Stream 5) Bonus for a doubled letter – 1 Java 8 Stream API – 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() ) ) ;
  • 300. @JosePaumard#Devoxx #J8Stream 5) Bonus for a doubled letter – 2 Java 8 Stream API – Rx Java // Bonus for double letter ToIntFunction<String> bonusForDoubleLetter = word -> Stream.of(first3.apply(word), last3.apply(word)) .flatMapToInt(Function.identity()) .map(scoreOfALetter) .max() .orElse(0) ;
  • 301. @JosePaumard#Devoxx #J8Stream 5) Bonus for a doubled letter – 2 Java 8 Stream API – Rx Java // Bonus for double letter Func1<String, Observable<Integer>> bonusForDoubleLetter = word -> Observable.just(first3.call(word), last3.call(word)) .flatMap(observable -> observable) .flatMap(scoreOfALetter) .reduce(Integer::max) ;
  • 302. @JosePaumard#Devoxx #J8Stream 6) Final score of a word Java 8 Stream API – 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);
  • 303. @JosePaumard#Devoxx #J8Stream 6) Final score of a word Java 8 Stream API – 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) ;
  • 304. @JosePaumard#Devoxx #J8Stream 7) Histogram of the scores Java 8 Stream API – Rx Java Function<Function<String, Integer>, Map<Integer, List<String>>> buildHistoOnScore = score -> shakespeareWords.stream().parallel() .filter(scrabbleWords::contains) .filter(checkBlanks) .collect( Collectors.groupingBy( score, () -> new TreeMap<>(Comparator.reverseOrder()), Collectors.toList() ) ) ;
  • 305. @JosePaumard#Devoxx #J8Stream 7) Histogram of the scores Java 8 Stream API – Rx Java Func1<Func1<String, Observable<Integer>>, Observable<TreeMap<Integer, List<String>>>> buildHistoOnScore = score -> Observable.from(() -> shakespeareWords.iterator()) .filter(scrabbleWords::contains) .filter(word -> checkBlanks.call(word).toBlocking().first()) .collect( () -> new TreeMap<Integer, List<String>>(Comparator.reverseOrder()), (TreeMap<Integer, List<String>> map, String word) -> { Integer key = score.call(word).toBlocking().first() ; List<String> list = map.get(key) ; if (list == null) { list = new ArrayList<String>() ; map.put(key, list) ; } list.add(word) ; }) ;
  • 306. @JosePaumard#Devoxx #J8Stream 8) Best word Java 8 Stream API – Rx Java // best key / value pairs List<Entry<Integer, List<String>>> finalList = buildHistoOnScore.apply(score3).entrySet() .stream() .limit(3) .collect(Collectors.toList()) ;
  • 307. @JosePaumard#Devoxx #J8Stream 8) Best word Java 8 Stream API – Rx Java // best key / value pairs List<Entry<Integer, List<String>>> finalList2 = buildHistoOnScore.call(score3) .flatMap(map -> Observable.from(() -> map.entrySet().iterator())) .take(3) .collect( () -> new ArrayList<Entry<Integer, List<String>>>(), (list, entry) -> { list.add(entry) ; } ) .toBlocking() .first() ;
  • 308. @JosePaumard#Devoxx #J8Stream 8) Best word Java 8 Stream API – Rx Java // best key / value pairs CountDownLatch latch = new CountDownLatch(3) ; List<Entry<Integer, List<String>>> finalList2 = buildHistoOnScore.call(score3) .flatMap(map -> Observable.from(() -> map.entrySet().iterator())) .take(3) .collect( () -> new ArrayList<Entry<Integer, List<String>>>(), (list, entry) -> { list.add(entry) ; latch.countDown() ; } ) .forEach(...) ; latch.await() ;
  • 309. @JosePaumard#Devoxx #J8Stream Patterns comparison Java 8 Stream API: clean, simple, factory methods for Collectors RxJava: flatMap calls, lack of factory methods Java 8 can easily go parallel, which is a plus
  • 310. @JosePaumard#Devoxx #J8Stream Performances Let us use JMH Standard tool for measuring code performance on the JVM Developed as an Open JDK tool By Aleksey Shipilev http://shipilev.net/ https://twitter.com/shipilev http://openjdk.java.net/projects/code-tools/jmh/ http://openjdk.java.net/projects/code-tools/jcstress/ https://www.parleys.com/tutorial/java-microbenchmark-harness-the-lesser-two-evils
  • 311. @JosePaumard#Devoxx #J8Stream JMH Easy to setup <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.11.1</version> </dependency>
  • 312. @JosePaumard#Devoxx #J8Stream JMH Easy to use @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MILLISECONDS) @Warmup(iterations=5) @Measurement(iterations=5) @Fork(3) public List<Entry<Integer, List<String>>> measureAverage() { // implementation to test }
  • 313. @JosePaumard#Devoxx #J8Stream JMH Launching a benchmark > mvn clean install > java –jar target/benchmark.jar
  • 314. @JosePaumard#Devoxx #J8Stream JMH 3 ways of measuring performances average execution time number of executions per second quantiles diagrams
  • 315. @JosePaumard#Devoxx #J8Stream Performances Average execution time Benchmark Mode Cnt Score Error Units NonParallelStreams avgt 100 29,027 ± 0,279 ms/op
  • 316. @JosePaumard#Devoxx #J8Stream Performances Average execution time Benchmark Mode Cnt Score Error Units NonParallelStreams avgt 100 29,027 ± 0,279 ms/op RxJava avgt 100 253,788 ± 1,421 ms/op
  • 317. @JosePaumard#Devoxx #J8Stream Performances Average execution time Benchmark Mode Cnt Score Error Units NonParallelStreams avgt 100 29,027 ± 0,279 ms/op RxJava avgt 100 253,788 ± 1,421 ms/op ParallelStreams avgt 100 7,624 ± 0,055 ms/op
  • 318. @JosePaumard#Devoxx #J8Stream Performances Average execution time RxJava spends a lot of time openning observables, due to the all flatMap patterns Benchmark Mode Cnt Score Error Units NonParallelStreams avgt 100 29,027 ± 0,279 ms/op RxJava avgt 100 253,788 ± 1,421 ms/op ParallelStreams avgt 100 7,624 ± 0,055 ms/op
  • 319. @JosePaumard#Devoxx #J8Stream Performances The bench is on Github: https://github.com/JosePaumard/jdk8-stream-rx-comparison
  • 321. @JosePaumard#Devoxx #J8Stream Java 8 Stream API Back to the definitions: 1) A Stream does not hold any data 2) A Stream does not modify its data
  • 322. @JosePaumard#Devoxx #J8Stream Java 8 Stream API Back to the definitions: 1) A Stream does not hold any data 2) A Stream does not modify its data How does a Stream work? 1) It connects to a source of data: one source = one stream 2) It consumes the data from the source: « pull mode »
  • 323. @JosePaumard#Devoxx #J8Stream Java 8 Stream API What about: - Connecting several streams to a single source?
  • 324. @JosePaumard#Devoxx #J8Stream Java 8 Stream API What about: - Connecting several streams to a single source? - Connecting several sources to a single stream?
  • 325. @JosePaumard#Devoxx #J8Stream Java 8 Stream API What about: - Connecting several streams to a single source? - Connecting several sources to a single stream? - Having a source that produces data whether or not a stream is connected to it
  • 326. @JosePaumard#Devoxx #J8Stream Java 8 Stream API What about: - Connecting several streams to a single source? - Connecting several sources to a single stream? - Having a source that produces data whether or not a stream is connected to it Clearly, the Stream API has not been made to handle this
  • 327. @JosePaumard#Devoxx #J8Stream Reactive Stream API This leads to the « reactive stream » API 3rd party API: Rx Java (and several other languages) Implementations available as a preview of JDK 9 Everything takes place in java.util.concurrent.Flow Available in the current JDK 9
  • 328. @JosePaumard#Devoxx #J8Stream Push mode stream Let us write a model for the source of data public interface Publisher<T> { public ... subscribe(Subscriber<T> subscriber); }
  • 329. @JosePaumard#Devoxx #J8Stream Push mode stream Let us write a model for the source of data As a subscriber I will want to unsubscribe So I need an object from the publisher on which I can call cancel() public interface Publisher<T> { public ... subscribe(Subscriber<T> subscriber); }
  • 330. @JosePaumard#Devoxx #J8Stream Push mode stream Let us write a model for the source of data The first idea that could come to mind is to return a Subscription object public interface Publisher<T> { public Subscription subscribe(Subscriber<T> subscriber); }
  • 331. @JosePaumard#Devoxx #J8Stream Push mode stream Let us write a model for the source of data But it will be a callback, to stay in an asynchronous world public interface Publisher<T> { public void subscribe(Subscriber<T> subscriber); }
  • 332. @JosePaumard#Devoxx #J8Stream Push mode stream Callback in the subscriber to get a subscription public interface Subscriber<T> { public void onSubscribe(Subscription subscription); }
  • 333. @JosePaumard#Devoxx #J8Stream Push mode stream Callback in the subscriber to get a subscription public interface Subscriber<T> { public void onSubscribe(Subscription subscription); } public interface Subscription { public void cancel(); }
  • 334. @JosePaumard#Devoxx #J8Stream Push mode stream The publisher might look like this public class SimplePublisher<T> implements Publisher<T> { private Set<Subscriber<T>> subs = ConcurrentHashMap.newKeySet(); public void subscribe(Subscriber<T> subscriber) { if (subs.add(subscriber)) { Subscription subscription = new SimpleSubscription(); subscriber.onSubscribe(subscription); } } }
  • 335. @JosePaumard#Devoxx #J8Stream Push mode stream In the subscribing code public class SimpleSubscriber<T> implements Subscriber<T> { private Subscription subscription; @Override public void onSubscribe(Subscription subscription) { this.subscription = subscription; } }
  • 336. @JosePaumard#Devoxx #J8Stream Push mode stream In the running code Publisher<String> publisher = ...; Subscriber<String> subscriber = ...; publisher.subscribe(subscriber); // some more code subscriber.getSubscription().cancel();
  • 337. @JosePaumard#Devoxx #J8Stream Push mode stream Callback in the subscriber to get a subscription I also need callbacks to get the data itself public interface Subscriber<T> { public void onSubscribe(Subscription subscription); }
  • 338. @JosePaumard#Devoxx #J8Stream Push mode stream Callback in the subscriber to get a subscription public interface Subscriber<T> { public void onSubscribe(Subscription subscription); }
  • 339. @JosePaumard#Devoxx #J8Stream Push mode stream Callback in the subscriber to get a subscription public interface Subscriber<T> { public void onSubscribe(Subscription subscription); public void onNext(T item); }
  • 340. @JosePaumard#Devoxx #J8Stream Push mode stream Callback in the subscriber to get a subscription public interface Subscriber<T> { public void onSubscribe(Subscription subscription); public void onNext(T item); public void onComplete(); }
  • 341. @JosePaumard#Devoxx #J8Stream Push mode stream Callback in the subscriber to get a subscription public interface Subscriber<T> { public void onSubscribe(Subscription subscription); public void onNext(T item); public void onComplete(); public void onError(Throwable throwable); }
  • 342. @JosePaumard#Devoxx #J8Stream Push mode stream Having a source that produces data independantly from its consumers implies to work in an asynchronous mode The API is built on callbacks
  • 343. @JosePaumard#Devoxx #J8Stream Several streams per source In « pull mode », it would not work, or would require the streams to be synchronized map Filter 1 Filter 2 Average map Filter 1 Histogram Data
  • 344. @JosePaumard#Devoxx #J8Stream Several streams per source In « pull mode », it would not work, or would require the streams to be synchronized In « push mode », it does not raise any problem map Filter 1 Filter 2 Average map Filter 1 Histogram Data
  • 345. @JosePaumard#Devoxx #J8Stream Several sources for a stream In « pull mode », it requires a special Spliterator map Filter 1 Filter 2 Average Data 1 Data 2
  • 346. @JosePaumard#Devoxx #J8Stream Several sources for a stream In « pull mode », it requires a special Spliterator In « push mode », since both sources are not synchronized, we may have problems map Filter 1 Filter 2 Average Data 1 Data 2
  • 347. @JosePaumard#Devoxx #J8Stream Push mode with several sources At some point in our data processing pipeline we want to see both sources as one, ie merged in some way
  • 348. @JosePaumard#Devoxx #J8Stream Push mode with several sources At some point in our data processing pipeline we want to see both sources as one, ie merged in some way How can we merge them if one source is faster than the other?
  • 349. @JosePaumard#Devoxx #J8Stream Push mode with several sources At some point in our data processing pipeline we want to see both sources as one, ie merged in some way How can we merge them if one source is faster than the other? We have all the strategies defined in RxJava
  • 350. @JosePaumard#Devoxx #J8Stream Merging sources in push mode 1) Decide to follow one of the streams, the first one 2) Combine the two last seen items, - everytime a new item is generated - or synchronized on a clock, or another source
  • 351. @JosePaumard#Devoxx #J8Stream Merging sources in push mode 1) Decide to follow one of the streams, the first one 2) Combine the two last seen items, - everytime a new item is generated - or synchronized on a clock, or another source Sampler, Debouncer, Throttler, …
  • 352. @JosePaumard#Devoxx #J8Stream The question of backpressure What will happen if a source is « too fast »? That is, a consumer cannot process data fast enough It leads to the same question of « backpressure »
  • 353. @JosePaumard#Devoxx #J8Stream Backpressure Several strategies: 1) Create a buffer 2) Synchronize on a clock, or a gate, that could be generated by the slow observer and sample, or windows, or debounce, or… 3) Try to slow down the source (can be done if I have the hand on both the producer and the consumer)
  • 354. @JosePaumard#Devoxx #J8Stream Backpressure There is code for that in the Subscription object The request() method is there to give information to the producer public interface Subscription { public void cancel(); public void request(long n); }
  • 355. @JosePaumard#Devoxx #J8Stream Backpressure There is code for that in the Subscription object The request() method is there to give information to the producer public void onNext(String element) { // process the element this.subscription.request(1L); }
  • 356. @JosePaumard#Devoxx #J8Stream Backpressure Several strategies: 1) Create a buffer 2) Synchronize on a clock, or a gate, that could be generated by the slow observer and sample, or windows, or debounce, or… 3) Try to slow down the source (can be done if I have the hand on both the producer and the consumer) 4) Have several observers in parallel and then merge the results
  • 357. @JosePaumard#Devoxx #J8Stream Reactive Streams New concept (at least in the JDK) New complexity, several use cases are possible Still under work (in the JDK and in 3rd party)
  • 358. @JosePaumard#Devoxx #J8Stream Reactive Streams links Some references on the reactive streams: http://www.reactive-streams.org/ http://reactivex.io/ https://github.com/reactive-streams/ http://openjdk.java.net/jeps/266 http://gee.cs.oswego.edu/dl/jsr166/dist/docs/index.html
  • 359. @JosePaumard#Devoxx #J8Stream Reactive Streams links In the classes currently available in the JSR 166 package: - The class Flow has the Publisher, Subscriber and Subscription interfaces, and the Processor interface - The class SubmissionPublisher, that implements Publisher, meant to be overriden or used as a component of a complete implementation Just made it to the OpenJDK 9
  • 361. @JosePaumard#Devoxx #J8Stream Conclusion Functional programming is in the mood From the pure performance point of view, things are not that simple Java 8 Streams have adopted a partial functional approach, which is probably a good trade off
  • 362. @JosePaumard#Devoxx #J8Stream Conclusion Java 8 Stream API: great API to process data in a « pull » mode The introduction of a « push » mode allows for many improvements (synchronization, backpressure) The backpressure question is relevant Loosing items ≠ loosing information!
  • 363. @JosePaumard#Devoxx #J8Stream Conclusion RxJava: rich and complex API many patterns are available in Java 8 Streams can run in Java 7 applications the « push » approach is very interesting choose your use case carefully, to avoir performance hits http://www.reactive-streams.org/ http://reactivex.io/ https://github.com/reactive-streams/
  • 364. @JosePaumard#Devoxx #J8Stream Conclusion Java 8 Streams: part of the JDK good performances, efficient memory footprint parallelization extensible through the Spliterator patterns
  • 365. @JosePaumard#Devoxx #J8Stream Conclusion Streams & Reactive Streams are very active topics Java Stream has been released with Java 8, improvements will be added in Java 9 and beyond Reactive Streams has several 3rd party implementations (RxJava) in several languages Will be part of Java 9
  • 367. Q/A