Java 8 Streams and Rx Java Comparison

Preview:

Citation preview

@JosePaumard

RxJavaJ8 Stream

Comparison:

patterns

performances

Why should we beinterested in the

Streams API?

@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 extractorCollectors.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 extractorCollectors.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 extractorCollectors.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 extractorCollectors.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 extractorCollectors.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 -> 3c -> 3d -> 1e -> 2f -> 1g -> 1i -> 7l -> 3o -> 2p -> 2r -> 2s -> 3t -> 1u -> 2x -> 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

// ArrayListSpliteratorpublic int characteristics() {

return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;

}

// HashMap.KeySpliteratorpublic 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 StreamsRxJava

@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 createan 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) ;

// intervalObservable<Long> timeSerie1 =

Observable.interval(1L, TimeUnit.MILLISECONDS) ; // serie of longs

// initial delay, then intervalObservable<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, // producerfinal Func1<Resource, Observable<T>> observableFactory, // functionfinal 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 - two1 – one1 - two2 – one2 – 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 - one1 - one2 - one3 - one4 - one5 - one5 - two6 - one6 - two7 - one7 – 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 subscriptionConnectableObservable<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, // ProducerAction2<R, T> collector) { } // BiConsumer

collect(ArrayList::new, // ProducerArrayList::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 eventsFunc1<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 eventsFunc1<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 ObservablevalueReady = 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, // onNextexception -> 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, // onNextexception -> 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), // onNextexception -> 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), // onNextexception -> 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 wordFunction<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 wordFunc1<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 Javainterface 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 letterToLongFunction<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 letterFunc1<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 wordFunction<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 wordFunc1<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 lettersFunction<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 lettersFunc1<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 letterToIntFunction<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 letterFunc1<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 boardFunction<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 boardFunc1<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 JavaFunc1<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 pairsList<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 pairsList<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 pairsCountDownLatch 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 UnitsNonParallelStreams avgt 100 29,027 ± 0,279 ms/op

@JosePaumard#Devoxx #J8Stream

Performances

Average execution time

Benchmark Mode Cnt Score Error UnitsNonParallelStreams avgt 100 29,027 ± 0,279 ms/opRxJava avgt 100 253,788 ± 1,421 ms/op

@JosePaumard#Devoxx #J8Stream

Performances

Average execution time

Benchmark Mode Cnt Score Error UnitsNonParallelStreams avgt 100 29,027 ± 0,279 ms/opRxJava avgt 100 253,788 ± 1,421 ms/opParallelStreams 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 UnitsNonParallelStreams avgt 100 29,027 ± 0,279 ms/opRxJava avgt 100 253,788 ± 1,421 ms/opParallelStreams 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.FlowAvailable 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;

@Overridepublic 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