journey's end

189
Journey’s End, JDK.IO, September 2016 Journey’s End Collection and Reduction in the Stream API JavaOne 2016 Maurice Naftalin @mauricenaftalin

Upload: maurice-naftalin

Post on 12-Jan-2017

19 views

Category:

Software


0 download

TRANSCRIPT

Journey’s End, JDK.IO, September 2016

Journey’s EndCollection and Reduction in the Stream API

JavaOne 2016Maurice Naftalin@mauricenaftalin

Journey’s End, JavaOne, September 2016

Maurice Naftalin

Journey’s End, JavaOne, September 2016

Maurice Naftalin

Java 5

Journey’s End, JavaOne, September 2016

Maurice Naftalin

Java 5 Java 8

Journey’s End, JavaOne, September 2016

Maurice Naftalin

Java 5 Java 8

Journey’s End, JavaOne, September 2016

Maurice Naftalin

Java 5 Java 8

2013

Journey’s End, JavaOne, September 2016

Maurice Naftalin

Java 5 Java 8

2014

Journey’s End, JavaOne, September 2016

Maurice Naftalin

Java 5 Java 8

2015

Journey’s End, JavaOne, September 2016

• Why collectors?

• Using the predefined collectors

• Worked problems

• Writing your own

Journey’s End – Agenda

Journey’s End, JavaOne, September 2016

Example Domain

city: City name: Stringage: int

Person

Person amy = new Person(Athens, "Amy", 21); ...

List<Person> people = Arrays.asList(jon, amy, bill);

Journey’s End, JavaOne, September 2016

Collectors: Journey’s End for a Stream

The Life of a Stream Element

• Born (at a spliterator)

• Transformed (by intermediate operations)

• Collected (by a terminal operation)

Journey’s End, JavaOne, September 2016

Collectors: Journey’s End for a Stream

The Life of a Stream Element

• Born (at a spliterator)

• Transformed (by intermediate operations)

• Collected (by a terminal operation)

Journey’s End, JavaOne, September 2016

Collectors: Journey’s End for a Stream

The Life of a Stream Element

• Born (at a spliterator)

• Transformed (by intermediate operations)

• Collected (by a terminal operation)

Journey’s End, JavaOne, September 2016

Collectors: Journey’s End for a Stream

?

The Life of a Stream Element

• Born (at a spliterator)

• Transformed (by intermediate operations)

• Collected (by a terminal operation)

Journey’s End, JavaOne, September 2016

Terminal Operations

– Search operations

– Side-effecting operations

– Reductions

Journey’s End, JavaOne, September 2016

Search Operations

Search operations -

• allMatch, anyMatch

• findAny, findFirst boolean allAdults = people.stream() .allMatch(p -> p.getAge() >= 21);

Journey’s End, JavaOne, September 2016

Side-effecting OperationsSide-effecting operations

• forEach, forEachOrderedpeople.stream() .forEach(System.out::println);

• So could we calculate total ages like this? int sum = 0; people.stream() .mapToInt(Person::getAge) .forEach(a -> { sum += a });

people.stream() .forEach(System.out::println);

int sum = 0; people.stream() .mapToInt(Person::getAge) .forEach(a -> { sum += a; });

Journey’s End, JavaOne, September 2016

Side-effecting OperationsSide-effecting operations

• forEach, forEachOrderedpeople.stream() .forEach(System.out::println);

• So could we calculate total ages like this? int sum = 0; people.stream() .mapToInt(Person::getAge) .forEach(a -> { sum += a });

people.stream() .forEach(System.out::println);

int sum = 0; people.stream() .mapToInt(Person::getAge) .forEach(a -> { sum += a; }); Don’t do this!

Journey’s End, JavaOne, September 2016

• Using an accumulator:

Reduction – Why?

int[] vals = new int[100];Arrays.setAll(vals, i -> i);

int sum = 0;for (int i = 0 ; i < vals.length ; i++) { sum += vals[i];}

Journey’s End, JavaOne, September 2016

• Using an accumulator:

Reduction – Why?

int[] vals = new int[100];Arrays.setAll(vals, i -> i);

int sum = 0;for (int i = 0 ; i < vals.length ; i++) { sum += vals[i];}

0 1 2 3

+

+

+

Journey’s End, JavaOne, September 2016

• Avoiding an accumulator:

Reduction – Why?

Journey’s End, JavaOne, September 2016

• Avoiding an accumulator:

Reduction – Why?

Journey’s End, JavaOne, September 2016

• Avoiding an accumulator:

Reduction – Why?

int[] vals = new int[100];Arrays.setAll(vals, i -> i);

OptionalInt sum = Arrays.stream(vals) .reduce((a,b) -> a + b);

+ +

+

1 2 30

Journey’s End, JavaOne, September 2016

• Avoiding an accumulator:

Reduction – Why?

int[] vals = new int[100];Arrays.setAll(vals, i -> i);

OptionalInt sum = Arrays.stream(vals) .reduce((a,b) -> a + b);

+ +

+

1 2 30

+

+

+

Journey’s End, JavaOne, September 2016

• Avoiding an accumulator:

Reduction – Why?

int[] vals = new int[100];Arrays.setAll(vals, i -> i);

OptionalInt sum = Arrays.stream(vals) .reduce((a,b) -> a + b);

BinaryOperator must be associative!

+ +

+

1 2 30

+

+

+

Journey’s End, JavaOne, September 2016

• Avoiding an accumulator:

Reduction – Why?

int[] vals = new int[100];Arrays.setAll(vals, i -> i);

OptionalInt sum = Arrays.stream(vals) .reduce((a,b) -> a + b);

BinaryOperator must be associative!a + (b + c) = (a + b) + c

+ +

+

1 2 30

+

+

+

Journey’s End, JavaOne, September 2016

0 1 2 3 4 5 6 7

+ +

+

+

+

+

+

Reduction – Why?

Journey’s End, JavaOne, September 2016

0 1 2 3 4 5 6 7

+ +

+

+

+

+

+

Reduction – Why?

Journey’s End, JavaOne, September 2016

0 1 2 3 4 5 6 7

+ +

+

+

+

+

+

Intermediate operations

Reduction – Why?

Journey’s End, JavaOne, September 2016

Reduction on Immutable Values

Reduction works on immutable values too

BigDecimal[] vals = new BigDecimal[100];Arrays.setAll(vals, i -> new BigDecimal(i));

Optional<BigDecimal> sum = Arrays.stream(vals) .reduce(BigDecimal::add);

Journey’s End, JavaOne, September 2016

Reduction to Collections?

Journey’s End, JavaOne, September 2016

Reduction to Collections?

This also works with collections – sort of ...

• for each element, client code creates a new empty collection and adds the single element to it

• combines collections using addAll

Journey’s End, JavaOne, September 2016

Reduction to Collections?

This also works with collections – sort of ...

• for each element, client code creates a new empty collection and adds the single element to it

• combines collections using addAll

We can do better!

Journey’s End, JavaOne, September 2016

Reduction over an Identity

BigDecimal[] vals = new BigDecimal[100];Arrays.setAll(vals, i -> new BigDecimal(i));

BigDecimal sum = Arrays.stream(vals) .reduce(BigDecimal.ZERO,BigDecimal::add);

Journey’s End, JavaOne, September 2016

Reduction over an Identity

BigDecimal[] vals = new BigDecimal[100];Arrays.setAll(vals, i -> new BigDecimal(i));

BigDecimal sum = Arrays.stream(vals) .reduce(BigDecimal.ZERO,BigDecimal::add);

Works for immutable objects:

Journey’s End, JavaOne, September 2016

Reduction over an Identity

+

+

+

0 1 2 3

+

+

+

0

4 5 6 7

0

++

+

Journey’s End, JavaOne, September 2016

Reduction to Collections?

Journey’s End, JavaOne, September 2016

Reduction to Collections?

Reduction over an identity doesn’t work with collections at all

• reduction reuses the identity element

Journey’s End, JavaOne, September 2016

Reduction to Collections?

Reduction over an identity doesn’t work with collections at all

• reduction reuses the identity element

We’ve got to do better!

Journey’s End, JavaOne, September 2016

The Answer – Collectors

a

a

a

e0 e1 e2 e3

a

a

a

e4 e5 e6 e7

aa

c

Journey’s End, JavaOne, September 2016

The Answer – Collectors

a

a

a

e0 e1 e2 e3

a

a

a

() -> []

e4 e5 e6 e7

aa

c

() -> []

Journey’s End, JavaOne, September 2016

The Answer – Collectors

a

a

a

e0 e1 e2 e3

a

a

a

() -> []

e4 e5 e6 e7

aa

c

a: accumulatorc: combiner

() -> []

Journey’s End, JavaOne, September 2016

Collectors

Journey’s End, JavaOne, September 2016

Collectors

So to define a collector, you need to provide

• Supplier

• Accumulator

• Combiner

Journey’s End, JavaOne, September 2016

Collectors

So to define a collector, you need to provide

• Supplier

• Accumulator

• Combiner

That sounds really hard!

Journey’s End, JavaOne, September 2016

Collectors

So to define a collector, you need to provide

• Supplier

• Accumulator

• Combiner

That sounds really hard!

Good then that we don’t have to do it

… very often

Journey’s End, JavaOne, September 2016

• Why collectors?

• Using the predefined collectors

• Worked problems

• Writing your own

Journey’s End – Agenda

Journey’s End, JavaOne, September 2016

Collectors API

Factory methods in the Collectors class. They produce standalone collectors, accumulating to:

• framework-supplied containers

• custom collections

• classification maps

Journey’s End, JavaOne, September 2016

Predefined Standalone Collectors – from factory methods in Collectors class

• toList(), toSet(), toMap(), joining()

• toMap(), toCollection()

• groupingBy(), partitioningBy(), groupingByConcurrent()

Using the Predefined Collectors

Journey’s End, JavaOne, September 2016

Predefined Standalone Collectors – from factory methods in Collectors class

• toList(), toSet(), toMap(), joining()

• toMap(), toCollection()

• groupingBy(), partitioningBy(), groupingByConcurrent()

Using the Predefined Collectors

framework providesthe Supplier

Journey’s End, JavaOne, September 2016

Predefined Standalone Collectors – from factory methods in Collectors class

• toList(), toSet(), toMap(), joining()

• toMap(), toCollection()

• groupingBy(), partitioningBy(), groupingByConcurrent()

Using the Predefined Collectors

user providesthe Supplier

framework providesthe Supplier

Journey’s End, JavaOne, September 2016

Predefined Standalone Collectors – from factory methods in Collectors class

• toList(), toSet(), toMap(), joining()

• toMap(), toCollection()

• groupingBy(), partitioningBy(), groupingByConcurrent()

Using the Predefined Collectors

user providesthe Supplier

framework providesthe Supplier

produce a classification map

Journey’s End, JavaOne, September 2016

Simple Collector – toSet()

Collectors.toSet()

people.stream().collect(Collectors.toSet())

Journey’s End, JavaOne, September 2016

Simple Collector – toSet()

Stream<Person>

Collectors.toSet()

Set<Person>

people.stream().collect(Collectors.toSet())

Collector<Person,?,Set<Person>>

Journey’s End, JavaOne, September 2016

Simple Collector – toSet()

Stream<Person>

Collectors.toSet()

Set<Person>

people.stream().collect(Collectors.toSet())

bill

Collector<Person,?,Set<Person>>

Journey’s End, JavaOne, September 2016

Simple Collector – toSet()

Stream<Person>

Collectors.toSet()

Set<Person>

people.stream().collect(Collectors.toSet())

bill

Collector<Person,?,Set<Person>>

Journey’s End, JavaOne, September 2016

Simple Collector – toSet()

Stream<Person>

Collectors.toSet()

Set<Person>

people.stream().collect(Collectors.toSet())

bill

Collector<Person,?,Set<Person>>

Journey’s End, JavaOne, September 2016

Simple Collector – toSet()

Stream<Person>

Collectors.toSet()

Set<Person>

people.stream().collect(Collectors.toSet())

billjon

Collector<Person,?,Set<Person>>

Journey’s End, JavaOne, September 2016

Simple Collector – toSet()

Stream<Person>

Collectors.toSet()

Set<Person>

people.stream().collect(Collectors.toSet())

billjon

Collector<Person,?,Set<Person>>

Journey’s End, JavaOne, September 2016

Simple Collector – toSet()

Stream<Person>

Collectors.toSet()

Set<Person>

people.stream().collect(Collectors.toSet())

amybill

jon

Collector<Person,?,Set<Person>>

Journey’s End, JavaOne, September 2016

Simple Collector – toSet()

Stream<Person>

Collectors.toSet()

Set<Person>

people.stream().collect(Collectors.toSet())

amy

billjon

Collector<Person,?,Set<Person>>

Journey’s End, JavaOne, September 2016

Simple Collector – toSet()

Collectors.toSet()

Set<Person>

people.stream().collect(Collectors.toSet())

amy

billjon

Collector<Person,?,Set<Person>>

Journey’s End, JavaOne, September 2016

Simple Collector – toSet()

Collectors.toSet()

Set<Person>{ , , }

people.stream().collect(Collectors.toSet())

amybilljon

Collector<Person,?,Set<Person>>

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

people.stream().collect( Collectors.toMap(

Person::getCity, Person::getName))

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

Collector<Person,?,Map<City,String>

people.stream().collect(toMap(Person::getCity,Person::getName))

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

Collector<Person,?,Map<City,String>

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

bill

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

bill

Person::getCity

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>London

bill

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>London

bill Person::getName

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>London “Bill”

bill Person::getName

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>London “Bill”

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

amy

London “Bill”

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

Athensamy

London “Bill”

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

“Amy”Athens

London “Bill”

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

“Amy”Athensjon

London “Bill”

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

“Amy”Athens

London “Bill”

Tulsa “Jon”

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

toMap() Map<City,String>

“Amy”Athens

London “Bill”

Tulsa “Jon”

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

toMap() Map<City,String>

“Amy”AthensLondon “Bill”

Tulsa “Jon”

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

“Amy”Athensjon

London “Bill”

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

“Amy”Athensjon

London “Bill”

Athens

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper)

Stream<Person>

toMap() Map<City,String>

“Amy”Athensjon

London

IllegalStateException

“Bill”

Athens

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction)

people.stream().collect( Collectors.toMap(

Person::getCity, Person::getName, (a,b) -> a.concat(b)))

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction)

Stream<Person>

toMap() Map<City,String>

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction)

Stream<Person>

toMap() Map<City,String>

“Amy”Athens

London “Bill”

Athens

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction)

Stream<Person>

toMap() Map<City,String>

“Amy”Athens

London “Bill”

Athens

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction)

Stream<Person>

toMap() Map<City,String>

“Amy”Athens

London “Bill”

“Jon”Athens

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction)

Stream<Person>

toMap() Map<City,String>

“Amy”Athens

London “Bill”

“Jon”

(a,b) -> a.concat(b)

Athens

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction)

Stream<Person>

toMap() Map<City,String>

Athens

London “Bill”

(a,b) -> a.concat(b)

“AmyJon”Athens

Journey’s End, JavaOne, September 2016

Collectors API

Factory methods in the Collectors class. They produce standalone collectors, accumulating to:

• framework-supplied containers

• custom collections

• classification maps

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction)

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction, Supplier<M> mapFactory)

people.stream().collect( Collectors.toMap(

Person::getCity, Person::getName, (a,b) -> a.concat(b), TreeMap::new))

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction, Supplier<M> mapFactory)

Stream<Person>

toMap() Map<City,String>

Journey’s End, JavaOne, September 2016

toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction, Supplier<M> mapFactory)

Stream<Person>

toMap() Map<City,String>

Journey’s End, JavaOne, September 2016

Collecting to Custom Collections

Journey’s End, JavaOne, September 2016

Collecting to Custom Collections

toCollection(Supplier<C> collectionFactory)

Journey’s End, JavaOne, September 2016

Collecting to Custom Collections

<C extends Collection<T>>toCollection(Supplier<C> collectionFactory)

Journey’s End, JavaOne, September 2016

Collecting to Custom Collections

returns

Collector<T,?,C>

<C extends Collection<T>>toCollection(Supplier<C> collectionFactory)

Journey’s End, JavaOne, September 2016

Collecting to Custom Collections

returns

Collector<T,?,C>

<C extends Collection<T>>

NavigableSet<String> sortedNames = people.stream().map(Person::getName) .collect(toCollection(TreeSet::new));

toCollection(Supplier<C> collectionFactory)

Journey’s End, JavaOne, September 2016

Collectors API

Factory methods in the Collectors class. They produce standalone collectors, accumulating to:

• framework-supplied containers

• custom collections

• classification maps

Journey’s End, JavaOne, September 2016

Collecting to Classification Maps

groupingBy

• simple

• with downstream

• with downstream and map factory

partitioningBy

Journey’s End, JavaOne, September 2016

groupingBy(Function<T,K> classifier)Uses the classifier function to make a classification mapping

Like toMap(), except that the values placed in the map are lists of the elements, one List corresponding to each classification key:

For example, use Person.getCity() to make a Map<City,List<Person>>

Map<City,List<Person>> peopleByCity = people.stream() .collect(Collectors.groupingBy(Person::getCity));

Journey’s End, JavaOne, September 2016

Stream<Person>

groupingBy() Map<City,List<Person>>

bill

jon

amy Athens

London

Collector<Person,?,Map<City,List<Person>

groupingBy(Function<Person,City>)

Classifier

Person→City

Journey’s End, JavaOne, September 2016

Stream<Person>

groupingBy() Map<City,List<Person>>

bill

jon

amy Athens

London

groupingBy(Function<Person,City>)

Journey’s End, JavaOne, September 2016

Stream<Person>

groupingBy() Map<City,List<Person>>

bill

jon

amy Athens

bill London

groupingBy(Function<Person,City>)

Journey’s End, JavaOne, September 2016

Stream<Person>

groupingBy() Map<City,List<Person>>

bill

jon

amy Athens

billLondon

groupingBy(Function<Person,City>)

[ ]

Journey’s End, JavaOne, September 2016

Stream<Person>

groupingBy() Map<City,List<Person>>

bill

jon

amy Athens [ ]

bill

amy

London

groupingBy(Function<Person,City>)

[ ]

Journey’s End, JavaOne, September 2016

Stream<Person>

groupingBy() Map<City,List<Person>>

bill

jon

amy Athens [ ]

[ , ]bill

amy

jonLondon

groupingBy(Function<Person,City>)

Journey’s End, JavaOne, September 2016

groupingBy() Map<City,List<Person>>

bill

jon

amy Athens [ ]

[ , ]bill

amy

jonLondon

groupingBy(Function<Person,City>)

Journey’s End, JavaOne, September 2016

groupingBy() Map<City,List<Person>>

bill

jon

amy

Athens [ ]

[ , ]bill

amy

jonLondon

groupingBy(Function<Person,City>)

Journey’s End, JavaOne, September 2016

groupingBy(Function<T,K> classifier)Uses the classifier function to make a classification mapping

Like toMap(), except that the values placed in the map are lists of the elements, one List corresponding to each classification key:

For example, use Person.getCity() to make a Map<City,List<Person>>

Map<City,List<Person>> peopleByCity = people.stream() .collect(Collectors.groupingBy(Person::getCity));

Journey’s End, JavaOne, September 2016

groupingBy(classifier, downstream)Uses the classifier function to make a classification mapping into a container defined by a downstream Collector

Like toMap(), except that the values placed in the map are containers of the elements, one container corresponding to each classification key:

For example, use Person.getCity() to make a Map<City,Set<Person>>

Map<City,List<Person>> peopleByCity = people.stream() .collect(Collectors.groupingBy(Person::getCity,toSet()));

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

Stream<Person>

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens

DownstreamCollector

—toSet()

Stream<Person>

Classifier

Person→City

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

Stream<Person>

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

Stream<Person>

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens

bill

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

Stream<Person>

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens

bill

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

Stream<Person>

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens

bill

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

Stream<Person>

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athensamy

bill

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

Stream<Person>

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens amy

bill

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

Stream<Person>

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens amy

bill

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

Stream<Person>

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens amy

jon bill

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

Stream<Person>

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens amy

jonbill

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens amy

jonbill

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens amy

jonbill

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens { }amy

{ , }jonbill

Journey’s End, JavaOne, September 2016

groupingBy(Function classifier,Collector downstream))

groupingBy()Map<City,Set<Person>>

bill

jon

amy

London

Athens { }amy

{ , }jonbill

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

Applies a mapping function to each input element before passing it to the downstream collector.

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

Applies a mapping function to each input element before passing it to the downstream collector.

Set<City> inhabited = people.stream() .collect(mapping(Person::getCity,toSet()));

Animation example

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

LondonStream<Person>

mapping()

bill

jon

amy Athens

Mapper

Person→City

DownstreamCollector

—toSet()

Stream<City>

Set<City>

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

LondonStream<Person>

mapping()

bill

jon

amy Athens

Set<City>

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

LondonStream<Person>

mapping()

bill

jon

amy Athens

LondonSet<City>

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

LondonStream<Person>

mapping()

bill

jon

amy Athens

Athens

LondonSet<City>

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

LondonStream<Person>

mapping()

bill

jon

amy Athens

Athens

LondonLondonSet<City>

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

London

mapping()

bill

jon

amy Athens

Athens

LondonLondonSet<City>

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

London

mapping()

bill

jon

amy

{ , }

Athens

AthensLondon

Set<City>

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

London

mapping()

bill

jon

amy

{ , }

Athens

AthensLondon

Set<City>

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

Applies a mapping function to each input element before passing it to the downstream collector.

Set<City> inhabited = people.stream() .collect(mapping(Person::getCity,toSet()));

Animation example

Journey’s End, JavaOne, September 2016

mapping(Function mapper,Collector downstream))

Applies a mapping function to each input element before passing it to the downstream collector.

Set<City> inhabited = people.stream() .collect(mapping(Person::getCity,toSet()));

Map<City,String> namesByCity = people.stream() .collect(groupingBy(Person::getCity,

mapping(Person::getName,joining()));

Animation example

Sensible example

Collectors’ Fair, JavaOne, October 2016

groupingBy()

bill

jon

amy Athens

billamyjon

London

Collector<Person,?,String>

Classifier

Person→City

JDK Collectors with Java 8 Streams

mapping()

Mapper

Person→String

Stream<String>

joining()

Collector<CharSequence,?,String>

Journey’s End, JavaOne, September 2016

Dualling Convenience Reductions

Terminal operation Collectors factory method

count() counting()

max() min()

maxBy() minBy()

sum() average()

summingXxx() averagingXxx()

summaryStatistics() summarizingXxx()

reduce(accumulator) reduce(identity, accumulator)

reduce(identity, accumulator, combiner)

reducing(accumulator) reducing(identity, accumulator)

reducing(identity, accumulator, combiner)

Journey’s End, JavaOne, September 2016

Concurrent Collection

Journey’s End, JavaOne, September 2016

Concurrent Collection

Journey’s End, JavaOne, September 2016

Thread safety is guaranteed by the framework

• Even for non-threadsafe containers!

Concurrent Collection

Journey’s End, JavaOne, September 2016

Thread safety is guaranteed by the framework

• Even for non-threadsafe containers!

But at a price... So what if your container is already threadsafe?

• A concurrentMap implementation, for example?

Concurrent Collection

Journey’s End, JavaOne, September 2016

Thread safety is guaranteed by the framework

• Even for non-threadsafe containers!

But at a price... So what if your container is already threadsafe?

• A concurrentMap implementation, for example?

Concurrent Collection

Journey’s End, JavaOne, September 2016

Thread safety is guaranteed by the framework

• Even for non-threadsafe containers!

But at a price... So what if your container is already threadsafe?

• A concurrentMap implementation, for example?

Every overload of toMap() and groupingBy() has a dual

• toConcurrentMap(...)

• groupingByConcurrent(...)

Concurrent Collection

Journey’s End, JavaOne, September 2016

• Why collectors?

• Using the predefined collectors

• Worked problems

• Writing your own

Journey’s End – Agenda

Most Popular Age

Most Popular Age

Optional<Integer> modalAge = populationByAge

Most Popular Age

Optional<Integer> modalAge = populationByAge .entrySet()

Most Popular Age

Optional<Integer> modalAge = populationByAge .entrySet() .stream()

Most Popular Age

Optional<Integer> modalAge = populationByAge .entrySet() .stream() .sorted(Entry.<Integer,Long>comparingByValue().reversed())

Most Popular Age

Optional<Integer> modalAge = populationByAge .entrySet() .stream() .sorted(Entry.<Integer,Long>comparingByValue().reversed()) .map(Entry::getKey)

Most Popular Age

Optional<Integer> modalAge = populationByAge .entrySet() .stream() .sorted(Entry.<Integer,Long>comparingByValue().reversed()) .map(Entry::getKey) .findFirst();

Most Popular Age

Most Popular Age

Optional<Integer> modalAge = populationByAge

Most Popular Age

Optional<Integer> modalAge = populationByAge .entrySet()

Most Popular Age

Optional<Integer> modalAge = populationByAge .entrySet() .stream()

Most Popular Age

Optional<Integer> modalAge = populationByAge .entrySet() .stream() .collect(maxBy(Comparator.comparing(Map.Entry::getValue)))

Most Popular Age

Optional<Integer> modalAge = populationByAge .entrySet() .stream() .collect(maxBy(Comparator.comparing(Map.Entry::getValue))) .map(Map.Entry::getKey);

Most Popular Age

Optional<Integer> modalAge = populationByAge .entrySet() .stream() .collect(maxBy(Comparator.comparing(Map.Entry::getValue))) .map(Map.Entry::getKey);

Map<Long, Set<Integer>> collect = people.stream() .collect(groupingBy(People::getAge, counting())) .entrySet() .stream() .collect(groupingBy(Entry::getValue,

mapping(Entry::getKey, toSet())));

Most Popular Ages

Journey’s End, JavaOne, September 2016

• Why collectors?

• Using the predefined collectors

• Worked problems

• Writing your own

Journey’s End – Agenda

Journey’s End, JavaOne, September 2016

Writing a Collector

Journey’s End, JavaOne, September 2016

Writing a Collector

Why would you want to?

Journey’s End, JavaOne, September 2016

Writing a Collector

Why would you want to?

• accumulate to a container that doesn’t implement Collection

Journey’s End, JavaOne, September 2016

Writing a Collector

Why would you want to?

• accumulate to a container that doesn’t implement Collection

• share state between values being collected

Journey’s End, JavaOne, September 2016

Oprah’s Problem

Journey’s End, JavaOne, September 2016

Oprah’s Problem

How to find a book?

Journey’s End, JavaOne, September 2016

Oprah’s Problem

How to find a book?

336640

144624

Page count 239640

3841104

172400

Journey’s End, JavaOne, September 2016

What Oprah Needs

New Earth 0Poisonwood Bible 336

Night 976A Fine Balance 1120

...

Cumulative page counts

Journey’s End, JavaOne, September 2016

Supplier and Accumulator

“NE” 336 0 “PB” 640 336[ ],

]“NE” 336 0[

][

aNewEarth thePoisonwoodBible

accumulator

accumulator

Journey’s End, JavaOne, September 2016

Combiner

,“Night” 144 976 “AFB” 624 1120 ]“NE” 336 0 “PB” 640 336[ , ,

Journey’s End, JavaOne, September 2016

Combiner

,“Night” 144 0[ “AFB” 624 144 ]“NE” 336 0 “PB” 640 336[ ],

,“Night” 144 976 “AFB” 624 1120 ]“NE” 336 0 “PB” 640 336[ , ,

Journey’s End, JavaOne, September 2016

Combiner

combiner

,“Night” 144 0[ “AFB” 624 144 ]“NE” 336 0 “PB” 640 336[ ],

,“Night” 144 976 “AFB” 624 1120 ]“NE” 336 0 “PB” 640 336[ , ,

Journey’s End, JavaOne, September 2016

Combiner

combiner

,“Night” 144 0[ “AFB” 624 144 ]“NE” 336 0 “PB” 640 336[ ],

,“Night” 144 976 “AFB” 624 1120 ]“NE” 336 0 “PB” 640 336[ , ,

976

Journey’s End, JavaOne, September 2016

Combiner

combiner

,“Night” 144 0[ “AFB” 624 144 ]“NE” 336 0 “PB” 640 336[ ],

,“Night” 144 976 “AFB” 624 1120 ]“NE” 336 0 “PB” 640 336[ , ,

976

Journey’s End, JavaOne, September 2016

Combiner

combiner

,“Night” 144 0[ “AFB” 624 144 ]“NE” 336 0 “PB” 640 336[ ],

,“Night” 144 976 “AFB” 624 1120 ]“NE” 336 0 “PB” 640 336[ , ,

Journey’s End, JavaOne, September 2016

Using Reduction Instead...

“New Earth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ]

newEarth poisonwood night

Journey’s End, JavaOne, September 2016

Using Reduction Instead...

“New Earth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ]

newEarth poisonwood night

Page count

Start displacement

Journey’s End, JavaOne, September 2016

Using Reduction Instead...

“New Earth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ]

]“New Earth” 336 0 “Poisonwood Bible” 640 0[ ] ]“Night” 144 0 ][[

newEarth poisonwood night

Journey’s End, JavaOne, September 2016

Using Reduction Instead...

“New Earth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ]

]“New Earth” 336 0 “Poisonwood Bible” 640 0[ ] ]“Night” 144 0 ][[

newEarth poisonwood night

Map

Journey’s End, JavaOne, September 2016

Using Reduction Instead...

“New Earth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ]

]“New Earth” 336 0 “Poisonwood Bible” 640 0[ ] ]“Night” 144 0 ][[

newEarth poisonwood night

Reduce

Map

Journey’s End, JavaOne, September 2016

Using Reduction Instead...

“New Earth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ]

]“New Earth” 336 0 “Poisonwood Bible” 640 0[ ] ]“Night” 144 0 ][[

PartialResult::new

ArrayDeque::new

newEarth poisonwood night

Reduce

Map

Journey’s End, JavaOne, September 2016

Using Reduction Instead...

“New Earth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ]

]“New Earth” 336 0 “Poisonwood Bible” 640 0[ ] ]“Night” 144 0 ][[

PartialResult::new

ArrayDeque::new

PartialResult::new

ArrayDeque::new

PartialResult::new

ArrayDeque::new

newEarth poisonwood night

Reduce

Journey’s End, JavaOne, September 2016

Performance of CollectorsDepends on the performance of accumulator and combiner functions

• toList(), toSet(), toCollection – performance will probably be dominated by accumulator, but remember that the framework will need to manage multithread access to non-threadsafe containers for the combine operation

toMap(), toConcurrentMap()

• map merging is slow. Resizing maps, especially concurrent maps, is particularly expensive. Whenever possible, presize all data structures, maps in particular.

Journey’s End, JavaOne, September 2016

ConclusionCollectors

• generalisation of reduction

• allow a functional style while continuing to work with a language based on mutation

• designed for composition

• flexible and powerful programming tool

• take a bit of getting used to! – but they’re worth it!

Journey’s End, JavaOne, September 2016

Questions?

Journey’s End, JavaOne, September 2016

Questions?