Journey’s End
Collection and Reduction in the Stream API
JavaOne, October 2015
Developer, designer, architect, teacher, learner, writer
Maurice Naftalin
Co-author
Current Projects
www.lambdafaq.org
2
• Why collectors?
• Using the predefined collectors
• Intermission!
• Worked problems
• Writing your own
Journey’s End – Agenda
Journey’s End, JavaOne, October 2015
Example Domain
city: City
name: String
age: int
Person
Person amy = new Person(Athens, "Amy", 21);

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

Journey’s End, JavaOne, October 2015
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, October 2015
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, October 2015
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, October 2015
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, October 2015
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, October 2015
Terminal Operations
– Search operations
– Side-effecting operations
– Reductions

Journey’s End, JavaOne, October 2015
Search Operations
Search operations -
• allMatch, anyMatch
• findAny, findFirst
boolean allAdults =
people.stream()

.allMatch(p -> p.getAge() >= 21);

Journey’s End, JavaOne, October 2015
Side-effecting Operations
Side-effecting operations
• forEach, forEachOrdered



people.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, October 2015
Side-effecting Operations
Side-effecting operations
• forEach, forEachOrdered



people.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, October 2015
• 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, October 2015
• 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, October 2015
• 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, October 2015
• Avoiding an accumulator:
Reduction – Why?
Journey’s End, JavaOne, October 2015
• Avoiding an accumulator:
Reduction – Why?
Journey’s End, JavaOne, October 2015
• 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, October 2015
• 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, October 2015
• 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, October 2015
• 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, October 2015
Why Reduction?
0 1 2 3 4 5 6 7
+ +
+
+
+
+
+
Journey’s End, JavaOne, October 2015
Why Reduction?
0 1 2 3 4 5 6 7
+ +
+
+
+
+
+
Journey’s End, JavaOne, October 2015
Why Reduction?
0 1 2 3 4 5 6 7
+ +
+
+
+
+
+
Intermediate operations
Journey’s End, JavaOne, October 2015
Reduction on ImmutableValues
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, October 2015
Reduction on ImmutableValues
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, October 2015
Reduction to Collections?
Journey’s End, JavaOne, October 2015
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, October 2015
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, October 2015
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, October 2015
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, October 2015
Reduction over an Identity
+
+
+
0 1 2 3
+
+
+
0
4 5 6 7
0
++
+
Journey’s End, JavaOne, October 2015
Reduction to Collections?
Journey’s End, JavaOne, October 2015
Reduction to Collections?
Reduction over an identity doesn’t work with
collections at all
• reduction reuses the identity element
Journey’s End, JavaOne, October 2015
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, October 2015
The Answer – Collectors
a
a
a
e0 e1 e2 e3
a
a
a
e4 e5 e6 e7
aa
c
a: accumulator
c: combiner
Journey’s End, JavaOne, October 2015
The Answer – Collectors
a
a
a
e0 e1 e2 e3
a
a
a
() -> []
e4 e5 e6 e7
aa
c
a: accumulator
c: combiner
() -> []
Journey’s End, JavaOne, October 2015
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, October 2015
Collectors API
Factory methods in the Collectors class.They
produce standalone collectors, accumulating to:
• framework-supplied containers;
• custom collections;
• classification maps.
• Why collectors?
• Using the predefined collectors
• Intermission!
• Worked problems
• Writing your own
Journey’s End – Agenda
Journey’s End, JavaOne, October 2015
Predefined Standalone Collectors – from factory
methods in Collections class
• toList(), toSet(), toMap(), joining()
• toMap(), toCollection()
groupingBy(), partitioningBy(),
groupingByConcurrent()
Using the Predefined Collectors
Journey’s End, JavaOne, October 2015
Predefined Standalone Collectors – from factory
methods in Collections class
• toList(), toSet(), toMap(), joining()
• toMap(), toCollection()
groupingBy(), partitioningBy(),
groupingByConcurrent()
Using the Predefined Collectors
framework provides
the Supplier
Journey’s End, JavaOne, October 2015
Predefined Standalone Collectors – from factory
methods in Collections class
• toList(), toSet(), toMap(), joining()
• toMap(), toCollection()
groupingBy(), partitioningBy(),
groupingByConcurrent()
Using the Predefined Collectors
user provides
the Supplier
framework provides
the Supplier
Journey’s End, JavaOne, October 2015
Predefined Standalone Collectors – from factory
methods in Collections class
• toList(), toSet(), toMap(), joining()
• toMap(), toCollection()
groupingBy(), partitioningBy(),
groupingByConcurrent()
Using the Predefined Collectors
user provides
the Supplier
framework provides
the Supplier
produce a 

classification map
Journey’s End, JavaOne, October 2015
Simple Collector – toSet()
Collectors.toSet()
people.stream().collect(Collectors.toSet())
Journey’s End, JavaOne, October 2015
Simple Collector – toSet()
Stream<Person>
Collectors.toSet()
Set<Person>
people.stream().collect(Collectors.toSet())
Collector<Person,?,Set<Person>>
Journey’s End, JavaOne, October 2015
Simple Collector – toSet()
Stream<Person>
Collectors.toSet()
Set<Person>
people.stream().collect(Collectors.toSet())
bill
Collector<Person,?,Set<Person>>
Journey’s End, JavaOne, October 2015
Simple Collector – toSet()
Stream<Person>
Collectors.toSet()
Set<Person>
people.stream().collect(Collectors.toSet())
bill
Collector<Person,?,Set<Person>>
Journey’s End, JavaOne, October 2015
Simple Collector – toSet()
Stream<Person>
Collectors.toSet()
Set<Person>
people.stream().collect(Collectors.toSet())
bill
Collector<Person,?,Set<Person>>
Journey’s End, JavaOne, October 2015
Simple Collector – toSet()
Stream<Person>
Collectors.toSet()
Set<Person>
people.stream().collect(Collectors.toSet())
bill
jon
Collector<Person,?,Set<Person>>
Journey’s End, JavaOne, October 2015
Simple Collector – toSet()
Stream<Person>
Collectors.toSet()
Set<Person>
people.stream().collect(Collectors.toSet())
bill
jon
Collector<Person,?,Set<Person>>
Journey’s End, JavaOne, October 2015
Simple Collector – toSet()
Stream<Person>
Collectors.toSet()
Set<Person>
people.stream().collect(Collectors.toSet())
amy
bill
jon
Collector<Person,?,Set<Person>>
Journey’s End, JavaOne, October 2015
Simple Collector – toSet()
Stream<Person>
Collectors.toSet()
Set<Person>
people.stream().collect(Collectors.toSet())
amy
bill
jon
Collector<Person,?,Set<Person>>
Journey’s End, JavaOne, October 2015
Simple Collector – toSet()
Collectors.toSet()
Set<Person>
people.stream().collect(Collectors.toSet())
amy
bill
jon
Collector<Person,?,Set<Person>>
Journey’s End, JavaOne, October 2015
Simple Collector – toSet()
Collectors.toSet()
Set<Person>
{ , , }
people.stream().collect(Collectors.toSet())
amybilljon
Collector<Person,?,Set<Person>>
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
people.stream().collect(
Collectors.toMap(
Person::getCity,

Person::getName))
Journey’s End, JavaOne, October 2015
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, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
Collector<Person,?,Map<City,String>
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
bill
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
bill
Person::getCity
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
London
bill
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
London
bill Person::getName
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
London “Bill”
bill Person::getName
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
London “Bill”
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
amy
London “Bill”
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
Athens
amy
London “Bill”
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
“Amy”Athens
London “Bill”
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
“Amy”Athens
jon
London “Bill”
Journey’s End, JavaOne, October 2015
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, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
toMap() Map<City,String>
“Amy”Athens
London “Bill”
Tulsa “Jon”
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
toMap() Map<City,String>
“Amy”Athens
London “Bill”
Tulsa “Jon”
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
“Amy”Athens
jon
London “Bill”
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
“Amy”Athens
jon
London “Bill”
Athens
Journey’s End, JavaOne, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper)
Stream<Person>
toMap() Map<City,String>
“Amy”Athens
jon
London
IllegalStateException
“Bill”
Athens
Journey’s End, JavaOne, October 2015
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, October 2015
toMap(Function<T,K> keyMapper,
Function<T,U> valueMapper
BinaryOperator<U> mergeFunction)
Stream<Person>
toMap() Map<City,String>
Journey’s End, JavaOne, October 2015
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, October 2015
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, October 2015
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, October 2015
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, October 2015
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, October 2015
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, October 2015
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, October 2015
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, October 2015
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, October 2015
Collecting to Custom Collections
<C extends Collection<T>>
NavigableSet<String> sortedNames = people.stream()
.map(Person::getName)

.collect(toCollection(TreeSet::new));
Journey’s End, JavaOne, October 2015
Collecting to Custom Collections
toCollection(Supplier<C> collectionFactory)
<C extends Collection<T>>
NavigableSet<String> sortedNames = people.stream()
.map(Person::getName)

.collect(toCollection(TreeSet::new));
Journey’s End, JavaOne, October 2015
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, October 2015
Collecting to Classification Maps
groupingBy
• simple
• with downstream
• with downstream and map factory
partitioningBy
Journey’s End, JavaOne, October 2015
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, October 2015
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, October 2015
Stream<Person>
groupingBy() Map<City,List<Person>>
bill
jon
amy Athens
London
groupingBy(Function<Person,City>)
Journey’s End, JavaOne, October 2015
Stream<Person>
groupingBy() Map<City,List<Person>>
bill
jon
amy Athens
bill London
groupingBy(Function<Person,City>)
Journey’s End, JavaOne, October 2015
Stream<Person>
groupingBy() Map<City,List<Person>>
bill
jon
amy Athens
billLondon
groupingBy(Function<Person,City>)
[ ]
Journey’s End, JavaOne, October 2015
Stream<Person>
groupingBy() Map<City,List<Person>>
bill
jon
amy Athens [ ]
bill
amy
London
groupingBy(Function<Person,City>)
[ ]
Journey’s End, JavaOne, October 2015
Stream<Person>
groupingBy() Map<City,List<Person>>
bill
jon
amy Athens [ ]
[ , ]bill
amy
jonLondon
groupingBy(Function<Person,City>)
Journey’s End, JavaOne, October 2015
groupingBy() Map<City,List<Person>>
bill
jon
amy Athens [ ]
[ , ]bill
amy
jonLondon
groupingBy(Function<Person,City>)
Journey’s End, JavaOne, October 2015
groupingBy() Map<City,List<Person>>
bill
jon
amy
Athens [ ]
[ , ]bill
amy
jonLondon
groupingBy(Function<Person,City>)
Journey’s End, JavaOne, October 2015
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, October 2015
groupingBy(classifier, downstream)
Map<City,List<Person>> peopleByCity = people.stream().

collect(Collectors.groupingBy(Person::getCity,toSet()));
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,Set<Person>> peopleByCity = people.stream().

collect(Collectors.groupingBy(Person::getCity,toSet()));
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
Stream<Person>
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens
Downstream
Collector
—
toSet()
Stream<Person
>
Classifier
Person→City
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
Stream<Person>
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
Stream<Person>
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens
bill
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
Stream<Person>
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens
bill
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
Stream<Person>
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens
bill
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
Stream<Person>
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athensamy
bill
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
Stream<Person>
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens amy
bill
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
Stream<Person>
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens amy
bill
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
Stream<Person>
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens amy
jon bill
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
Stream<Person>
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens amy
jon
bill
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens amy
jon
bill
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens amy
jon
bill
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens { }amy
{ , }jonbill
Journey’s End, JavaOne, October 2015
groupingBy(Function classifier,Collector downstream))
groupingBy()
Map<City,Set<Person>
bill
jon
amy
London
Athens { }amy
{ , }jonbill
Journey’s End, JavaOne, October 2015
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
mapping(Function mapper,Collector downstream))
LondonStream<Person>
mapping()
bill
jon
amy
41
Athens
Mapper
Person→City
Downstream
Collector
—
toSet()
Stream<City>
Set<City>
mapping(Function mapper,Collector downstream))
LondonStream<Person>
mapping()
bill
jon
amy
41
Athens
Set<City>
mapping(Function mapper,Collector downstream))
LondonStream<Person>
mapping()
bill
jon
amy
41
Athens
London
Set<City>
mapping(Function mapper,Collector downstream))
LondonStream<Person>
mapping()
bill
jon
amy
41
Athens
Athens
London
Set<City>
mapping(Function mapper,Collector downstream))
LondonStream<Person>
mapping()
bill
jon
amy
41
Athens
Athens
LondonLondon
Set<City>
mapping(Function mapper,Collector downstream))
London
mapping()
bill
jon
amy
41
Athens
Athens
LondonLondon
Set<City>
mapping(Function mapper,Collector downstream))
London
mapping()
bill
jon
amy
{ ,
}
41
Athens
Athens
London
Set<City>
mapping(Function mapper,Collector downstream))
London
mapping()
bill
jon
amy
{ ,
}
41
Athens
Athens
London
Set<City>
Dualling Convenience Reductions
• Collectors.counting()
• Collectors.maxBy
• Collectors.minBy
• Collectors.summarizingXXX
42
Journey’s End, JavaOne, October 2015
Concurrent Collection
Journey’s End, JavaOne, October 2015
Thread safety is guaranteed by the framework
• Even for non-threadsafe containers!
Concurrent Collection
Journey’s End, JavaOne, October 2015
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, October 2015
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, October 2015
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
• Why collectors?
• Using the predefined collectors
• Intermission!
• Worked problems
• Writing your own
Journey’s End – Agenda
• Why collectors?
• Using the predefined collectors
• Intermission!
• Worked problems
• Writing your own
Journey’s End – Agenda
Journey’s End, JavaOne, October 2015
Writing a Collector
Why would you want to?
Journey’s End, JavaOne, October 2015
Writing a Collector
Why would you want to?
• accumulate to a container that doesn’t implement Collection
Journey’s End, JavaOne, October 2015
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, October 2015
Oprah’s Problem
Journey’s End, JavaOne, October 2015
Oprah’s Problem
How to find a book?
Journey’s End, JavaOne, October 2015
Oprah’s Problem
How to find a book?
336
640
144
624
Page count 239
640
384
1104
172
400
Journey’s End, JavaOne, October 2015
What Oprah Needs
New Earth 0
Poisonwood Bible 336
Night 976
A Fine Balance 1120
...
Cumulative
page counts
Supplier and Accumulator
“NE” 336 0 “PB” 640 336[ ],
]“NE” 336 0[
][
aNewEarth thePoisonwoodBible
accumulator
accumulator
Combiner
,“Night” 144 976 “AFB” 624 1120 ]“NE” 336 0 “PB” 640 336[ , ,
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[ , ,
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[ , ,
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
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
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[ , ,
Using Reduction Instead...
“New Earth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ]
newEarth poisonwood night
Using Reduction Instead...
“New Earth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ]
newEarth poisonwood night
Page count
Start displacement
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
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
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
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
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, October 2015
Performance of Collectors
Depends 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, October 2015
Conclusion
Collectors
• 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, October 2015
Questions?

Journey's End – Collection and Reduction in the Stream API

  • 1.
    Journey’s End Collection andReduction in the Stream API JavaOne, October 2015
  • 2.
    Developer, designer, architect,teacher, learner, writer Maurice Naftalin Co-author Current Projects www.lambdafaq.org 2
  • 3.
    • Why collectors? •Using the predefined collectors • Intermission! • Worked problems • Writing your own Journey’s End – Agenda
  • 4.
    Journey’s End, JavaOne,October 2015 Example Domain city: City name: String age: int Person Person amy = new Person(Athens, "Amy", 21);
 ... List<Person> people = Arrays.asList(jon, amy, bill);

  • 5.
    Journey’s End, JavaOne,October 2015 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)
  • 6.
    Journey’s End, JavaOne,October 2015 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)
  • 7.
    Journey’s End, JavaOne,October 2015 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)
  • 8.
    Journey’s End, JavaOne,October 2015 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)
  • 9.
    Journey’s End, JavaOne,October 2015 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)
  • 10.
    Journey’s End, JavaOne,October 2015 Terminal Operations – Search operations – Side-effecting operations – Reductions

  • 11.
    Journey’s End, JavaOne,October 2015 Search Operations Search operations - • allMatch, anyMatch • findAny, findFirst boolean allAdults = people.stream()
 .allMatch(p -> p.getAge() >= 21);

  • 12.
    Journey’s End, JavaOne,October 2015 Side-effecting Operations Side-effecting operations • forEach, forEachOrdered
 
 people.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; });

  • 13.
    Journey’s End, JavaOne,October 2015 Side-effecting Operations Side-effecting operations • forEach, forEachOrdered
 
 people.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!
  • 14.
    Journey’s End, JavaOne,October 2015 • 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]; }

  • 15.
    Journey’s End, JavaOne,October 2015 • 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 + +
  • 16.
    Journey’s End, JavaOne,October 2015 • 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 + + +
  • 17.
    Journey’s End, JavaOne,October 2015 • Avoiding an accumulator: Reduction – Why?
  • 18.
    Journey’s End, JavaOne,October 2015 • Avoiding an accumulator: Reduction – Why?
  • 19.
    Journey’s End, JavaOne,October 2015 • 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
  • 20.
    Journey’s End, JavaOne,October 2015 • 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 + + +
  • 21.
    Journey’s End, JavaOne,October 2015 • 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 + + +
  • 22.
    Journey’s End, JavaOne,October 2015 • 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 + + +
  • 23.
    Journey’s End, JavaOne,October 2015 Why Reduction? 0 1 2 3 4 5 6 7 + + + + + + +
  • 24.
    Journey’s End, JavaOne,October 2015 Why Reduction? 0 1 2 3 4 5 6 7 + + + + + + +
  • 25.
    Journey’s End, JavaOne,October 2015 Why Reduction? 0 1 2 3 4 5 6 7 + + + + + + + Intermediate operations
  • 26.
    Journey’s End, JavaOne,October 2015 Reduction on ImmutableValues 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);

  • 27.
    Journey’s End, JavaOne,October 2015 Reduction on ImmutableValues 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);

  • 28.
    Journey’s End, JavaOne,October 2015 Reduction to Collections?
  • 29.
    Journey’s End, JavaOne,October 2015 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
  • 30.
    Journey’s End, JavaOne,October 2015 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!
  • 31.
    Journey’s End, JavaOne,October 2015 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);

  • 32.
    Journey’s End, JavaOne,October 2015 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:
  • 33.
    Journey’s End, JavaOne,October 2015 Reduction over an Identity + + + 0 1 2 3 + + + 0 4 5 6 7 0 ++ +
  • 34.
    Journey’s End, JavaOne,October 2015 Reduction to Collections?
  • 35.
    Journey’s End, JavaOne,October 2015 Reduction to Collections? Reduction over an identity doesn’t work with collections at all • reduction reuses the identity element
  • 36.
    Journey’s End, JavaOne,October 2015 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!
  • 37.
    Journey’s End, JavaOne,October 2015 The Answer – Collectors a a a e0 e1 e2 e3 a a a e4 e5 e6 e7 aa c a: accumulator c: combiner
  • 38.
    Journey’s End, JavaOne,October 2015 The Answer – Collectors a a a e0 e1 e2 e3 a a a () -> [] e4 e5 e6 e7 aa c a: accumulator c: combiner () -> []
  • 39.
    Journey’s End, JavaOne,October 2015 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
  • 40.
    Journey’s End, JavaOne,October 2015 Collectors API Factory methods in the Collectors class.They produce standalone collectors, accumulating to: • framework-supplied containers; • custom collections; • classification maps.
  • 41.
    • Why collectors? •Using the predefined collectors • Intermission! • Worked problems • Writing your own Journey’s End – Agenda
  • 42.
    Journey’s End, JavaOne,October 2015 Predefined Standalone Collectors – from factory methods in Collections class • toList(), toSet(), toMap(), joining() • toMap(), toCollection() groupingBy(), partitioningBy(), groupingByConcurrent() Using the Predefined Collectors
  • 43.
    Journey’s End, JavaOne,October 2015 Predefined Standalone Collectors – from factory methods in Collections class • toList(), toSet(), toMap(), joining() • toMap(), toCollection() groupingBy(), partitioningBy(), groupingByConcurrent() Using the Predefined Collectors framework provides the Supplier
  • 44.
    Journey’s End, JavaOne,October 2015 Predefined Standalone Collectors – from factory methods in Collections class • toList(), toSet(), toMap(), joining() • toMap(), toCollection() groupingBy(), partitioningBy(), groupingByConcurrent() Using the Predefined Collectors user provides the Supplier framework provides the Supplier
  • 45.
    Journey’s End, JavaOne,October 2015 Predefined Standalone Collectors – from factory methods in Collections class • toList(), toSet(), toMap(), joining() • toMap(), toCollection() groupingBy(), partitioningBy(), groupingByConcurrent() Using the Predefined Collectors user provides the Supplier framework provides the Supplier produce a 
 classification map
  • 46.
    Journey’s End, JavaOne,October 2015 Simple Collector – toSet() Collectors.toSet() people.stream().collect(Collectors.toSet())
  • 47.
    Journey’s End, JavaOne,October 2015 Simple Collector – toSet() Stream<Person> Collectors.toSet() Set<Person> people.stream().collect(Collectors.toSet()) Collector<Person,?,Set<Person>>
  • 48.
    Journey’s End, JavaOne,October 2015 Simple Collector – toSet() Stream<Person> Collectors.toSet() Set<Person> people.stream().collect(Collectors.toSet()) bill Collector<Person,?,Set<Person>>
  • 49.
    Journey’s End, JavaOne,October 2015 Simple Collector – toSet() Stream<Person> Collectors.toSet() Set<Person> people.stream().collect(Collectors.toSet()) bill Collector<Person,?,Set<Person>>
  • 50.
    Journey’s End, JavaOne,October 2015 Simple Collector – toSet() Stream<Person> Collectors.toSet() Set<Person> people.stream().collect(Collectors.toSet()) bill Collector<Person,?,Set<Person>>
  • 51.
    Journey’s End, JavaOne,October 2015 Simple Collector – toSet() Stream<Person> Collectors.toSet() Set<Person> people.stream().collect(Collectors.toSet()) bill jon Collector<Person,?,Set<Person>>
  • 52.
    Journey’s End, JavaOne,October 2015 Simple Collector – toSet() Stream<Person> Collectors.toSet() Set<Person> people.stream().collect(Collectors.toSet()) bill jon Collector<Person,?,Set<Person>>
  • 53.
    Journey’s End, JavaOne,October 2015 Simple Collector – toSet() Stream<Person> Collectors.toSet() Set<Person> people.stream().collect(Collectors.toSet()) amy bill jon Collector<Person,?,Set<Person>>
  • 54.
    Journey’s End, JavaOne,October 2015 Simple Collector – toSet() Stream<Person> Collectors.toSet() Set<Person> people.stream().collect(Collectors.toSet()) amy bill jon Collector<Person,?,Set<Person>>
  • 55.
    Journey’s End, JavaOne,October 2015 Simple Collector – toSet() Collectors.toSet() Set<Person> people.stream().collect(Collectors.toSet()) amy bill jon Collector<Person,?,Set<Person>>
  • 56.
    Journey’s End, JavaOne,October 2015 Simple Collector – toSet() Collectors.toSet() Set<Person> { , , } people.stream().collect(Collectors.toSet()) amybilljon Collector<Person,?,Set<Person>>
  • 57.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) people.stream().collect( Collectors.toMap( Person::getCity,
 Person::getName))
  • 58.
    Journey’s End, JavaOne,October 2015 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))
  • 59.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> Collector<Person,?,Map<City,String>
  • 60.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> bill
  • 61.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> bill Person::getCity
  • 62.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> London bill
  • 63.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> London bill Person::getName
  • 64.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> London “Bill” bill Person::getName
  • 65.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> London “Bill”
  • 66.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> amy London “Bill”
  • 67.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> Athens amy London “Bill”
  • 68.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> “Amy”Athens London “Bill”
  • 69.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> “Amy”Athens jon London “Bill”
  • 70.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> “Amy”Athens London “Bill” Tulsa “Jon”
  • 71.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) toMap() Map<City,String> “Amy”Athens London “Bill” Tulsa “Jon”
  • 72.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) toMap() Map<City,String> “Amy”Athens London “Bill” Tulsa “Jon”
  • 73.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String>
  • 74.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> “Amy”Athens jon London “Bill”
  • 75.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> “Amy”Athens jon London “Bill” Athens
  • 76.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper) Stream<Person> toMap() Map<City,String> “Amy”Athens jon London IllegalStateException “Bill” Athens
  • 77.
    Journey’s End, JavaOne,October 2015 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)))
  • 78.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction) Stream<Person> toMap() Map<City,String>
  • 79.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction) Stream<Person> toMap() Map<City,String> “Amy”Athens London “Bill” Athens
  • 80.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction) Stream<Person> toMap() Map<City,String> “Amy”Athens London “Bill” Athens
  • 81.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction) Stream<Person> toMap() Map<City,String> “Amy”Athens London “Bill” “Jon” Athens
  • 82.
    Journey’s End, JavaOne,October 2015 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
  • 83.
    Journey’s End, JavaOne,October 2015 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
  • 84.
    Journey’s End, JavaOne,October 2015 Collectors API Factory methods in the Collectors class.They produce standalone collectors, accumulating to: • framework-supplied containers; • custom collections; • classification maps.
  • 85.
    Journey’s End, JavaOne,October 2015 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))
  • 86.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction, Supplier<M> mapFactory) Stream<Person> toMap() Map<City,String>
  • 87.
    Journey’s End, JavaOne,October 2015 toMap(Function<T,K> keyMapper, Function<T,U> valueMapper BinaryOperator<U> mergeFunction, Supplier<M> mapFactory) Stream<Person> toMap() Map<City,String>
  • 88.
    Journey’s End, JavaOne,October 2015 Collecting to Custom Collections <C extends Collection<T>> NavigableSet<String> sortedNames = people.stream() .map(Person::getName)
 .collect(toCollection(TreeSet::new));
  • 89.
    Journey’s End, JavaOne,October 2015 Collecting to Custom Collections toCollection(Supplier<C> collectionFactory) <C extends Collection<T>> NavigableSet<String> sortedNames = people.stream() .map(Person::getName)
 .collect(toCollection(TreeSet::new));
  • 90.
    Journey’s End, JavaOne,October 2015 Collectors API Factory methods in the Collectors class.They produce standalone collectors, accumulating to: • framework-supplied containers; • custom collections; • classification maps.
  • 91.
    Journey’s End, JavaOne,October 2015 Collecting to Classification Maps groupingBy • simple • with downstream • with downstream and map factory partitioningBy
  • 92.
    Journey’s End, JavaOne,October 2015 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));
  • 93.
    Journey’s End, JavaOne,October 2015 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
  • 94.
    Journey’s End, JavaOne,October 2015 Stream<Person> groupingBy() Map<City,List<Person>> bill jon amy Athens London groupingBy(Function<Person,City>)
  • 95.
    Journey’s End, JavaOne,October 2015 Stream<Person> groupingBy() Map<City,List<Person>> bill jon amy Athens bill London groupingBy(Function<Person,City>)
  • 96.
    Journey’s End, JavaOne,October 2015 Stream<Person> groupingBy() Map<City,List<Person>> bill jon amy Athens billLondon groupingBy(Function<Person,City>) [ ]
  • 97.
    Journey’s End, JavaOne,October 2015 Stream<Person> groupingBy() Map<City,List<Person>> bill jon amy Athens [ ] bill amy London groupingBy(Function<Person,City>) [ ]
  • 98.
    Journey’s End, JavaOne,October 2015 Stream<Person> groupingBy() Map<City,List<Person>> bill jon amy Athens [ ] [ , ]bill amy jonLondon groupingBy(Function<Person,City>)
  • 99.
    Journey’s End, JavaOne,October 2015 groupingBy() Map<City,List<Person>> bill jon amy Athens [ ] [ , ]bill amy jonLondon groupingBy(Function<Person,City>)
  • 100.
    Journey’s End, JavaOne,October 2015 groupingBy() Map<City,List<Person>> bill jon amy Athens [ ] [ , ]bill amy jonLondon groupingBy(Function<Person,City>)
  • 101.
    Journey’s End, JavaOne,October 2015 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));
  • 102.
    Journey’s End, JavaOne,October 2015 groupingBy(classifier, downstream) Map<City,List<Person>> peopleByCity = people.stream().
 collect(Collectors.groupingBy(Person::getCity,toSet())); 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,Set<Person>> peopleByCity = people.stream().
 collect(Collectors.groupingBy(Person::getCity,toSet()));
  • 103.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) Stream<Person> groupingBy() Map<City,Set<Person> bill jon amy London Athens Downstream Collector — toSet() Stream<Person > Classifier Person→City
  • 104.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) Stream<Person> groupingBy() Map<City,Set<Person> bill jon amy London Athens
  • 105.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) Stream<Person> groupingBy() Map<City,Set<Person> bill jon amy London Athens bill
  • 106.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) Stream<Person> groupingBy() Map<City,Set<Person> bill jon amy London Athens bill
  • 107.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) Stream<Person> groupingBy() Map<City,Set<Person> bill jon amy London Athens bill
  • 108.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) Stream<Person> groupingBy() Map<City,Set<Person> bill jon amy London Athensamy bill
  • 109.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) Stream<Person> groupingBy() Map<City,Set<Person> bill jon amy London Athens amy bill
  • 110.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) Stream<Person> groupingBy() Map<City,Set<Person> bill jon amy London Athens amy bill
  • 111.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) Stream<Person> groupingBy() Map<City,Set<Person> bill jon amy London Athens amy jon bill
  • 112.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) Stream<Person> groupingBy() Map<City,Set<Person> bill jon amy London Athens amy jon bill
  • 113.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) groupingBy() Map<City,Set<Person> bill jon amy London Athens amy jon bill
  • 114.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) groupingBy() Map<City,Set<Person> bill jon amy London Athens amy jon bill
  • 115.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) groupingBy() Map<City,Set<Person> bill jon amy London Athens { }amy { , }jonbill
  • 116.
    Journey’s End, JavaOne,October 2015 groupingBy(Function classifier,Collector downstream)) groupingBy() Map<City,Set<Person> bill jon amy London Athens { }amy { , }jonbill
  • 117.
    Journey’s End, JavaOne,October 2015 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
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
    Dualling Convenience Reductions •Collectors.counting() • Collectors.maxBy • Collectors.minBy • Collectors.summarizingXXX 42
  • 127.
    Journey’s End, JavaOne,October 2015 Concurrent Collection
  • 128.
    Journey’s End, JavaOne,October 2015 Thread safety is guaranteed by the framework • Even for non-threadsafe containers! Concurrent Collection
  • 129.
    Journey’s End, JavaOne,October 2015 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
  • 130.
    Journey’s End, JavaOne,October 2015 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
  • 131.
    Journey’s End, JavaOne,October 2015 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
  • 132.
    • Why collectors? •Using the predefined collectors • Intermission! • Worked problems • Writing your own Journey’s End – Agenda
  • 133.
    • Why collectors? •Using the predefined collectors • Intermission! • Worked problems • Writing your own Journey’s End – Agenda
  • 134.
    Journey’s End, JavaOne,October 2015 Writing a Collector Why would you want to?
  • 135.
    Journey’s End, JavaOne,October 2015 Writing a Collector Why would you want to? • accumulate to a container that doesn’t implement Collection
  • 136.
    Journey’s End, JavaOne,October 2015 Writing a Collector Why would you want to? • accumulate to a container that doesn’t implement Collection • share state between values being collected
  • 137.
    Journey’s End, JavaOne,October 2015 Oprah’s Problem
  • 138.
    Journey’s End, JavaOne,October 2015 Oprah’s Problem How to find a book?
  • 139.
    Journey’s End, JavaOne,October 2015 Oprah’s Problem How to find a book? 336 640 144 624 Page count 239 640 384 1104 172 400
  • 140.
    Journey’s End, JavaOne,October 2015 What Oprah Needs New Earth 0 Poisonwood Bible 336 Night 976 A Fine Balance 1120 ... Cumulative page counts
  • 141.
    Supplier and Accumulator “NE”336 0 “PB” 640 336[ ], ]“NE” 336 0[ ][ aNewEarth thePoisonwoodBible accumulator accumulator
  • 142.
    Combiner ,“Night” 144 976“AFB” 624 1120 ]“NE” 336 0 “PB” 640 336[ , ,
  • 143.
    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[ , ,
  • 144.
    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[ , ,
  • 145.
    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
  • 146.
    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
  • 147.
    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[ , ,
  • 148.
    Using Reduction Instead... “NewEarth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ] newEarth poisonwood night
  • 149.
    Using Reduction Instead... “NewEarth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ] newEarth poisonwood night Page count Start displacement
  • 150.
    Using Reduction Instead... “NewEarth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ] ]“New Earth” 336 0 “Poisonwood Bible” 640 0[ ] ]“Night” 144 0 ][[ newEarth poisonwood night
  • 151.
    Using Reduction Instead... “NewEarth” 336 0 “Poisonwood Bible” 640 336[ , “Night” 144 976, ] ]“New Earth” 336 0 “Poisonwood Bible” 640 0[ ] ]“Night” 144 0 ][[ newEarth poisonwood night Map
  • 152.
    Using Reduction Instead... “NewEarth” 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
  • 153.
    Using Reduction Instead... “NewEarth” 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
  • 154.
    Using Reduction Instead... “NewEarth” 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
  • 155.
    Journey’s End, JavaOne,October 2015 Performance of Collectors Depends 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.
  • 156.
    Journey’s End, JavaOne,October 2015 Conclusion Collectors • 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!
  • 157.
    Journey’s End, JavaOne,October 2015 Questions?