@JosePaumard
in Java &
Going reactive
@JosePaumard#J8RXS
Agenda
1) To present the concepts on which the Stream API has
been built
2) See the main patterns, what can be done with it
@JosePaumard#J8RXS
Agenda
1) To present the concepts on which the Stream API has
been built
2) See the main patterns, what can be done with it
3) What is missing to become reactive?
4) What is in the work for Java 9?
@JosePaumard#J8RXS
Questions?
#J8RXS
@JosePaumard
@JosePaumard
Data Processing
Java 8
Stream API
@JosePaumard#J8RXS
What is a Stream?
 A new concept in Java 8
 An interface (or several interfaces)
 Goals:
• To provide an implemenation of the map / filter / reduce
• Simple to use
• Efficient (memory, computation)
@JosePaumard#J8RXS
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#J8RXS
Patterns to create a Stream
 There are many patterns to create a Stream
List<Person> people = Arrays.asList(p1, p2, p3);
@JosePaumard#J8RXS
Patterns to create a Stream
 There are many patterns to create a Stream
List<Person> people = Arrays.asList(p1, p2, p3);
Stream<Person> stream = people.stream();
@JosePaumard#J8RXS
Patterns to create a Stream
 There are many 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#J8RXS
Patterns to create a Stream
 There are many 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);
Stream<String> words = Pattern.compile(" ").splitAsStream(book);
@JosePaumard#J8RXS
Patterns to create a Stream
 There are many 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);
Stream<String> words = Pattern.compile(" ").splitAsStream(book);
Stream<String> lines = Files.lines(Paths.get("alice-in-wonderland.txt"));
@JosePaumard#J8RXS
Patterns to use a Stream
 Map filter reduce with the Stream API
 « a Stream does not hold any data »
List<Person> people = Arrays.asList(p1, p2, p3);
double average = people.stream()
.filter(person -> person.getCity().equals("San Francisco"))
.mapToInt(Person::getAge)
.filter(age -> age > 20)
.average().get();
@JosePaumard#J8RXS
Patterns to use a Stream
 Map filter reduce with the Stream API
 « a Stream does not hold any data »
List<Person> people = Arrays.asList(p1, p2, p3);
double average = people.stream() // Stream<Person>
.filter(person -> person.getCity().equals("San Francisco"))
.mapToInt(Person::getAge)
.filter(age -> age > 20)
.average().get();
@JosePaumard#J8RXS
Patterns to use a Stream
 Map filter reduce with the Stream API
 « a Stream does not hold any data »
List<Person> people = Arrays.asList(p1, p2, p3);
double average = people.stream() // Stream<Person>
.filter(person -> person.getCity().equals("San Francisco"))
.mapToInt(Person::getAge) // IntStream
.filter(age -> age > 20)
.average().get();
@JosePaumard#J8RXS
Patterns to use a Stream
 Map filter reduce with the Stream API
 « a Stream does not hold any data »
List<Person> people = Arrays.asList(p1, p2, p3);
double average = people.stream() // Stream<Person>
.filter(person -> person.getCity().equals("San Francisco"))
.mapToInt(Person::getAge) // IntStream
.filter(age -> age > 20) // IntStream
.average().get();
@JosePaumard#J8RXS
Patterns to use a Stream
 Map filter reduce with the Stream API
 « a Stream does not hold any data »
List<Person> people = Arrays.asList(p1, p2, p3);
double average = people.stream() // Stream<Person>
.filter(person -> person.getCity().equals("San Francisco"))
.mapToInt(Person::getAge) // IntStream
.filter(age -> age > 20) // IntStream
.average().get(); // double
@JosePaumard#J8RXS
Patterns to use a Stream
 Map filter reduce with the Stream API
 An operation that returns a Stream does not process any
data
List<Person> people = Arrays.asList(p1, p2, p3);
double average = people.stream() // Stream<Person>
.filter(person -> person.getCity().equals("San Francisco"))
.mapToInt(Person::getAge) // IntStream
.filter(age -> age > 20) // IntStream
.average().get(); // double
@JosePaumard#J8RXS
« a Stream does not hold any data »
« a Stream does not hold any data » is a very powerful
paradygm
 It brings the notion of lazyness
 And optimizations!
@JosePaumard#J8RXS
Back to our initial program
 Efficient (memory, computation)
 Two things about streams:
1) A Stream does not hold any data
2) A Stream does not modify its data
@JosePaumard#J8RXS
Back to our initial program
 Efficient (memory, computation)
 Two things about streams:
1) A Stream does not hold any data
2) A Stream does not modify its data
@JosePaumard#J8RXS
Back to our initial program
 Efficient (memory, computation)
 Two things about streams:
1) A Stream does not hold any data
2) A Stream does not modify its data
@JosePaumard#J8RXS
Back to our initial program
 Efficient (memory, computation)
 Two things about streams:
1) A Stream does not hold any data
2) A Stream does not modify its data  Parallelism!
@JosePaumard#J8RXS
Going parallel
 Back to our previous example
List<Person> people = Arrays.asList(p1, p2, p3);
double average = people.stream().parallel()
.filter(person -> person.getCity().equals("San Francisco"))
.mapToInt(Person::getAge)
.filter(age -> age > 20)
.average().get();
@JosePaumard#J8RXS
What about non-standard sources?
A Stream is built on two things:
- A Spliterator (split – iterator)
@JosePaumard#J8RXS
What about non-standard sources?
A Stream is built on two things:
- A Spliterator (split – iterator)
- A ReferencePipeline (the implementation)
@JosePaumard#J8RXS
The Spliterator
 The Spliterator holds a special word: characteristics
@JosePaumard#J8RXS
The Spliterator
 The Spliterator holds 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#J8RXS
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#J8RXS
The Spliterator
 The Spliterator holds a special word: characteristics
 This word is used for optimization
people.stream()
.sorted() // quicksort?
.collect(Collectors.toList());
@JosePaumard#J8RXS
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#J8RXS
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#J8RXS
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#J8RXS
The characteristics can change
 Each Stream object in a pipeline has its own characteristics
@JosePaumard#J8RXS
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#J8RXS
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#J8RXS
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#J8RXS
What about non-standard sources?
 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#J8RXS
What about non-standard sources?
 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 processings
long estimateSize(); // can return 0
int characteristics(); // returns a constant
}
@JosePaumard#J8RXS
Spliterators on spliterators
 Building a Spliterator on another Spliterator allows:
Grouping: [1, 2, 3, 4, 5, …] ->
[[1, 2, 3], [4, 5, 6], [7, 8, 9], …]
@JosePaumard#J8RXS
Spliterators on spliterators
 Building a Spliterator on another Spliterator allows:
Rolling: [1, 2, 3, 4, 5, …] ->
[[1, 2, 3], [2, 3, 4], [3, 4, 5], …]
@JosePaumard#J8RXS
Spliterators on spliterators
 Building a Spliterator on another Spliterator allows:
Zipping: [1, 2, 3, …], [a, b, c, …] ->
[F[1, a], F[2, b], F[3, c], …]
@JosePaumard#J8RXS
Spliterators on spliterators
 Building a Spliterator on another Spliterator allows:
Zipping [1, 2, 3, …], [a, b, c, …] ->
+ grouping: [[F[1, a], F[2, b], F[3, c]],
[F[4, d], F[5, e], F[6, f]], …]
@JosePaumard#J8RXS
Spliterators on spliterators
 Building a Spliterator on another Spliterator allows:
Zipping [1, 2, 3, …], [a, b, c, …] ->
+ rolling: [[F[1, a], F[2, b], F[3, c]],
[F[2, b], F[3, c], F[4, d]], …]
@JosePaumard#J8RXS
Java 8 Stream API
 Simple, readable patterns
 Fast and efficient (with more to come)
 A Stream looks like a Collection, but it is not
 The Spliterator can be implemented to connect a Stream to
« non-standard » sources of data
 Or to change the way the data is analyzed
Java 8
Stream API
and beyond
@JosePaumard#J8RXS
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#J8RXS
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#J8RXS
Java 8 Stream API
What about:
- Connecting several streams to a single source?
@JosePaumard#J8RXS
Java 8 Stream API
What about:
- Connecting several streams to a single source?
- Connecting several sources to a single stream?
@JosePaumard#J8RXS
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#J8RXS
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#J8RXS
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 on the JSR166 web site
@JosePaumard#J8RXS
Push mode stream
 Let us write a model for the source of data
public interface Publisher<T> {
public ... subscribe(Subscriber<T> subscriber);
}
@JosePaumard#J8RXS
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#J8RXS
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#J8RXS
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#J8RXS
Push mode stream
 Callback in the subscriber to get a subscription
public interface Subscriber<T> {
public void onSubscribe(Subscription subscription);
}
@JosePaumard#J8RXS
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#J8RXS
Push mode stream
 The publisher might look like this
public class SimplePublisher<T> implements Publisher<T> {
private Set<Subscriber<T>> subscribers = ConcurrentHashMap.newKeySet();
public void subscribe(Subscriber<T> subscriber) {
if (subscribers.add(subscriber)) {
Subscription subscription = new SimpleSubscription();
subscriber.onSubscribe(subscription);
}
}
}
@JosePaumard#J8RXS
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#J8RXS
Push mode stream
 In the running code
Publisher<String> publisher = ...;
Subscriber<String> subscriber = ...;
publisher.subscribe(subscriber);
// some more code
subscriber.getSubscription().cancel();
@JosePaumard#J8RXS
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#J8RXS
Push mode stream
 Callback in the subscriber to get a subscription
public interface Subscriber<T> {
public void onSubscribe(Subscription subscription);
}
@JosePaumard#J8RXS
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#J8RXS
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#J8RXS
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#J8RXS
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#J8RXS
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#J8RXS
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#J8RXS
Several sources for a stream
 In « pull mode », it requires a special Spliterator
map Filter 1 Filter 2 Average
Data 1
Data 2
@JosePaumard#J8RXS
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#J8RXS
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#J8RXS
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#J8RXS
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?
 Several strategies are possible
@JosePaumard#J8RXS
Merging sources in push mode
1) Decide to follow one of the data publishers, the first one
@JosePaumard#J8RXS
Merging sources in push mode
1) Decide to follow one of the data publishers, the first one
Use case: identical requests on several DNS, or on several
Rest Services
The first to give the answer is the winner!
And makes the others useless
@JosePaumard#J8RXS
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
@JosePaumard#J8RXS
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
Source 1
Source 2
Merge
@JosePaumard#J8RXS
Merging sources in push mode
1) Decide to follow one of the streams, the first one
2) Combine the two last seen items,
or synchronized on the second source (for instance)
Source 1
Source 2
Merge
@JosePaumard#J8RXS
Merging sources in push mode
 This second approach brings the idea of synchronizing on a
source
 A source can play the role of a clock
@JosePaumard#J8RXS
Merging sources in push mode
 Let us build a sampler
Source
Clock
Merge
@JosePaumard#J8RXS
Merging sources in push mode
 Let us build a sampler with a function
Source
Clock
Merge
@JosePaumard#J8RXS
Merging sources in push mode
 Or a « debouncer »
Source
Clock
Merge
@JosePaumard#J8RXS
Merging sources in push mode
 There is no limit to what can be done with two independant
sources merged into one
 The synchronization-on-a-clock can be used to « slow
down » a source
@JosePaumard#J8RXS
A central question
 What will happen if a source is « too fast »?
 That is, a consumer cannot process data fast enough
 It leads to the question of « backpressure »
@JosePaumard#J8RXS
Backpressure
 Several strategies:
1) Create a buffer
@JosePaumard#J8RXS
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…
@JosePaumard#J8RXS
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#J8RXS
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#J8RXS
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#J8RXS
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#J8RXS
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#J8RXS
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 (Class Flow)
@JosePaumard#J8RXS
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
@JosePaumard#J8RXS
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#J8RXS
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

Going reactive in java

  • 1.
  • 2.
    @JosePaumard#J8RXS Agenda 1) To presentthe concepts on which the Stream API has been built 2) See the main patterns, what can be done with it
  • 3.
    @JosePaumard#J8RXS Agenda 1) To presentthe concepts on which the Stream API has been built 2) See the main patterns, what can be done with it 3) What is missing to become reactive? 4) What is in the work for Java 9?
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
    @JosePaumard#J8RXS What is aStream?  A new concept in Java 8  An interface (or several interfaces)  Goals: • To provide an implemenation of the map / filter / reduce • Simple to use • Efficient (memory, computation)
  • 10.
    @JosePaumard#J8RXS Definition of aStream 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
  • 11.
    @JosePaumard#J8RXS Patterns to createa Stream  There are many patterns to create a Stream List<Person> people = Arrays.asList(p1, p2, p3);
  • 12.
    @JosePaumard#J8RXS Patterns to createa Stream  There are many patterns to create a Stream List<Person> people = Arrays.asList(p1, p2, p3); Stream<Person> stream = people.stream();
  • 13.
    @JosePaumard#J8RXS Patterns to createa Stream  There are many 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);
  • 14.
    @JosePaumard#J8RXS Patterns to createa Stream  There are many 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); Stream<String> words = Pattern.compile(" ").splitAsStream(book);
  • 15.
    @JosePaumard#J8RXS Patterns to createa Stream  There are many 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); Stream<String> words = Pattern.compile(" ").splitAsStream(book); Stream<String> lines = Files.lines(Paths.get("alice-in-wonderland.txt"));
  • 16.
    @JosePaumard#J8RXS Patterns to usea Stream  Map filter reduce with the Stream API  « a Stream does not hold any data » List<Person> people = Arrays.asList(p1, p2, p3); double average = people.stream() .filter(person -> person.getCity().equals("San Francisco")) .mapToInt(Person::getAge) .filter(age -> age > 20) .average().get();
  • 17.
    @JosePaumard#J8RXS Patterns to usea Stream  Map filter reduce with the Stream API  « a Stream does not hold any data » List<Person> people = Arrays.asList(p1, p2, p3); double average = people.stream() // Stream<Person> .filter(person -> person.getCity().equals("San Francisco")) .mapToInt(Person::getAge) .filter(age -> age > 20) .average().get();
  • 18.
    @JosePaumard#J8RXS Patterns to usea Stream  Map filter reduce with the Stream API  « a Stream does not hold any data » List<Person> people = Arrays.asList(p1, p2, p3); double average = people.stream() // Stream<Person> .filter(person -> person.getCity().equals("San Francisco")) .mapToInt(Person::getAge) // IntStream .filter(age -> age > 20) .average().get();
  • 19.
    @JosePaumard#J8RXS Patterns to usea Stream  Map filter reduce with the Stream API  « a Stream does not hold any data » List<Person> people = Arrays.asList(p1, p2, p3); double average = people.stream() // Stream<Person> .filter(person -> person.getCity().equals("San Francisco")) .mapToInt(Person::getAge) // IntStream .filter(age -> age > 20) // IntStream .average().get();
  • 20.
    @JosePaumard#J8RXS Patterns to usea Stream  Map filter reduce with the Stream API  « a Stream does not hold any data » List<Person> people = Arrays.asList(p1, p2, p3); double average = people.stream() // Stream<Person> .filter(person -> person.getCity().equals("San Francisco")) .mapToInt(Person::getAge) // IntStream .filter(age -> age > 20) // IntStream .average().get(); // double
  • 21.
    @JosePaumard#J8RXS Patterns to usea Stream  Map filter reduce with the Stream API  An operation that returns a Stream does not process any data List<Person> people = Arrays.asList(p1, p2, p3); double average = people.stream() // Stream<Person> .filter(person -> person.getCity().equals("San Francisco")) .mapToInt(Person::getAge) // IntStream .filter(age -> age > 20) // IntStream .average().get(); // double
  • 22.
    @JosePaumard#J8RXS « a Streamdoes not hold any data » « a Stream does not hold any data » is a very powerful paradygm  It brings the notion of lazyness  And optimizations!
  • 23.
    @JosePaumard#J8RXS Back to ourinitial program  Efficient (memory, computation)  Two things about streams: 1) A Stream does not hold any data 2) A Stream does not modify its data
  • 24.
    @JosePaumard#J8RXS Back to ourinitial program  Efficient (memory, computation)  Two things about streams: 1) A Stream does not hold any data 2) A Stream does not modify its data
  • 25.
    @JosePaumard#J8RXS Back to ourinitial program  Efficient (memory, computation)  Two things about streams: 1) A Stream does not hold any data 2) A Stream does not modify its data
  • 26.
    @JosePaumard#J8RXS Back to ourinitial program  Efficient (memory, computation)  Two things about streams: 1) A Stream does not hold any data 2) A Stream does not modify its data  Parallelism!
  • 27.
    @JosePaumard#J8RXS Going parallel  Backto our previous example List<Person> people = Arrays.asList(p1, p2, p3); double average = people.stream().parallel() .filter(person -> person.getCity().equals("San Francisco")) .mapToInt(Person::getAge) .filter(age -> age > 20) .average().get();
  • 28.
    @JosePaumard#J8RXS What about non-standardsources? A Stream is built on two things: - A Spliterator (split – iterator)
  • 29.
    @JosePaumard#J8RXS What about non-standardsources? A Stream is built on two things: - A Spliterator (split – iterator) - A ReferencePipeline (the implementation)
  • 30.
    @JosePaumard#J8RXS The Spliterator  TheSpliterator holds a special word: characteristics
  • 31.
    @JosePaumard#J8RXS The Spliterator  TheSpliterator holds 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; }
  • 32.
    @JosePaumard#J8RXS The Spliterator  TheSpliterator 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; }
  • 33.
    @JosePaumard#J8RXS The Spliterator  TheSpliterator holds a special word: characteristics  This word is used for optimization people.stream() .sorted() // quicksort? .collect(Collectors.toList());
  • 34.
    @JosePaumard#J8RXS The Spliterator  TheSpliterator holds a special word: characteristics  This word is used for optimization people.stream() .sorted() // quicksort? It depends on SORTED == 0 .collect(Collectors.toList());
  • 35.
    @JosePaumard#J8RXS The Spliterator  TheSpliterator holds a special word: characteristics  This word is used for optimization SortedSet<Person> people = ...; people.stream() .sorted() // SORTED == 1, no quicksort .collect(Collectors.toList());
  • 36.
    @JosePaumard#J8RXS The Spliterator  TheSpliterator holds a special word: characteristics  This word is used for optimization ArrayList<Person> people = ...; people.stream() .sorted() // SORTED == 0, quicksort .collect(Collectors.toList());
  • 37.
    @JosePaumard#J8RXS The characteristics canchange  Each Stream object in a pipeline has its own characteristics
  • 38.
    @JosePaumard#J8RXS The characteristics canchange  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 -
  • 39.
    @JosePaumard#J8RXS The characteristics canchange  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 -
  • 40.
    @JosePaumard#J8RXS The characteristics canchange  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 -
  • 41.
    @JosePaumard#J8RXS What about non-standardsources?  The Spliterator is meant to be overriden public interface Spliterator<T> { boolean tryAdvance(Consumer<? super T> action) ; Spliterator<T> trySplit() ; long estimateSize(); int characteristics(); }
  • 42.
    @JosePaumard#J8RXS What about non-standardsources?  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 processings long estimateSize(); // can return 0 int characteristics(); // returns a constant }
  • 43.
    @JosePaumard#J8RXS Spliterators on spliterators Building a Spliterator on another Spliterator allows: Grouping: [1, 2, 3, 4, 5, …] -> [[1, 2, 3], [4, 5, 6], [7, 8, 9], …]
  • 44.
    @JosePaumard#J8RXS Spliterators on spliterators Building a Spliterator on another Spliterator allows: Rolling: [1, 2, 3, 4, 5, …] -> [[1, 2, 3], [2, 3, 4], [3, 4, 5], …]
  • 45.
    @JosePaumard#J8RXS Spliterators on spliterators Building a Spliterator on another Spliterator allows: Zipping: [1, 2, 3, …], [a, b, c, …] -> [F[1, a], F[2, b], F[3, c], …]
  • 46.
    @JosePaumard#J8RXS Spliterators on spliterators Building a Spliterator on another Spliterator allows: Zipping [1, 2, 3, …], [a, b, c, …] -> + grouping: [[F[1, a], F[2, b], F[3, c]], [F[4, d], F[5, e], F[6, f]], …]
  • 47.
    @JosePaumard#J8RXS Spliterators on spliterators Building a Spliterator on another Spliterator allows: Zipping [1, 2, 3, …], [a, b, c, …] -> + rolling: [[F[1, a], F[2, b], F[3, c]], [F[2, b], F[3, c], F[4, d]], …]
  • 48.
    @JosePaumard#J8RXS Java 8 StreamAPI  Simple, readable patterns  Fast and efficient (with more to come)  A Stream looks like a Collection, but it is not  The Spliterator can be implemented to connect a Stream to « non-standard » sources of data  Or to change the way the data is analyzed
  • 49.
  • 50.
    @JosePaumard#J8RXS Java 8 StreamAPI Back to the definitions: 1) A Stream does not hold any data 2) A Stream does not modify its data
  • 51.
    @JosePaumard#J8RXS Java 8 StreamAPI 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 »
  • 52.
    @JosePaumard#J8RXS Java 8 StreamAPI What about: - Connecting several streams to a single source?
  • 53.
    @JosePaumard#J8RXS Java 8 StreamAPI What about: - Connecting several streams to a single source? - Connecting several sources to a single stream?
  • 54.
    @JosePaumard#J8RXS Java 8 StreamAPI 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
  • 55.
    @JosePaumard#J8RXS Java 8 StreamAPI 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
  • 56.
    @JosePaumard#J8RXS 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 on the JSR166 web site
  • 57.
    @JosePaumard#J8RXS Push mode stream Let us write a model for the source of data public interface Publisher<T> { public ... subscribe(Subscriber<T> subscriber); }
  • 58.
    @JosePaumard#J8RXS 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); }
  • 59.
    @JosePaumard#J8RXS 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); }
  • 60.
    @JosePaumard#J8RXS 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); }
  • 61.
    @JosePaumard#J8RXS Push mode stream Callback in the subscriber to get a subscription public interface Subscriber<T> { public void onSubscribe(Subscription subscription); }
  • 62.
    @JosePaumard#J8RXS 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(); }
  • 63.
    @JosePaumard#J8RXS Push mode stream The publisher might look like this public class SimplePublisher<T> implements Publisher<T> { private Set<Subscriber<T>> subscribers = ConcurrentHashMap.newKeySet(); public void subscribe(Subscriber<T> subscriber) { if (subscribers.add(subscriber)) { Subscription subscription = new SimpleSubscription(); subscriber.onSubscribe(subscription); } } }
  • 64.
    @JosePaumard#J8RXS 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; } }
  • 65.
    @JosePaumard#J8RXS Push mode stream In the running code Publisher<String> publisher = ...; Subscriber<String> subscriber = ...; publisher.subscribe(subscriber); // some more code subscriber.getSubscription().cancel();
  • 66.
    @JosePaumard#J8RXS 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); }
  • 67.
    @JosePaumard#J8RXS Push mode stream Callback in the subscriber to get a subscription public interface Subscriber<T> { public void onSubscribe(Subscription subscription); }
  • 68.
    @JosePaumard#J8RXS 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); }
  • 69.
    @JosePaumard#J8RXS 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(); }
  • 70.
    @JosePaumard#J8RXS 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); }
  • 71.
    @JosePaumard#J8RXS 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
  • 72.
    @JosePaumard#J8RXS Several streams persource  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
  • 73.
    @JosePaumard#J8RXS Several streams persource  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
  • 74.
    @JosePaumard#J8RXS Several sources fora stream  In « pull mode », it requires a special Spliterator map Filter 1 Filter 2 Average Data 1 Data 2
  • 75.
    @JosePaumard#J8RXS Several sources fora 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
  • 76.
    @JosePaumard#J8RXS Push mode withseveral sources  At some point in our data processing pipeline we want to see both sources as one, ie merged in some way
  • 77.
    @JosePaumard#J8RXS Push mode withseveral 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?
  • 78.
    @JosePaumard#J8RXS Push mode withseveral 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?  Several strategies are possible
  • 79.
    @JosePaumard#J8RXS Merging sources inpush mode 1) Decide to follow one of the data publishers, the first one
  • 80.
    @JosePaumard#J8RXS Merging sources inpush mode 1) Decide to follow one of the data publishers, the first one Use case: identical requests on several DNS, or on several Rest Services The first to give the answer is the winner! And makes the others useless
  • 81.
    @JosePaumard#J8RXS Merging sources inpush 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
  • 82.
    @JosePaumard#J8RXS Merging sources inpush 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 Source 1 Source 2 Merge
  • 83.
    @JosePaumard#J8RXS Merging sources inpush mode 1) Decide to follow one of the streams, the first one 2) Combine the two last seen items, or synchronized on the second source (for instance) Source 1 Source 2 Merge
  • 84.
    @JosePaumard#J8RXS Merging sources inpush mode  This second approach brings the idea of synchronizing on a source  A source can play the role of a clock
  • 85.
    @JosePaumard#J8RXS Merging sources inpush mode  Let us build a sampler Source Clock Merge
  • 86.
    @JosePaumard#J8RXS Merging sources inpush mode  Let us build a sampler with a function Source Clock Merge
  • 87.
    @JosePaumard#J8RXS Merging sources inpush mode  Or a « debouncer » Source Clock Merge
  • 88.
    @JosePaumard#J8RXS Merging sources inpush mode  There is no limit to what can be done with two independant sources merged into one  The synchronization-on-a-clock can be used to « slow down » a source
  • 89.
    @JosePaumard#J8RXS A central question What will happen if a source is « too fast »?  That is, a consumer cannot process data fast enough  It leads to the question of « backpressure »
  • 90.
  • 91.
    @JosePaumard#J8RXS 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…
  • 92.
    @JosePaumard#J8RXS 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)
  • 93.
    @JosePaumard#J8RXS Backpressure  There iscode 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); }
  • 94.
    @JosePaumard#J8RXS Backpressure  There iscode 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); }
  • 95.
    @JosePaumard#J8RXS 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
  • 96.
    @JosePaumard#J8RXS Reactive Streams  Newconcept (at least in the JDK)  New complexity, several use cases are possible  Still under work (in the JDK and in 3rd party)
  • 97.
    @JosePaumard#J8RXS 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 (Class Flow)
  • 98.
    @JosePaumard#J8RXS 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
  • 99.
    @JosePaumard#J8RXS Conclusion  Java 8Stream 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!
  • 100.
    @JosePaumard#J8RXS 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
  • 101.
  • 102.