Java Reference
In-Depth Information
Suppose you want to get a list of names, grouping them by gender. You need to use the second version of the
groupingBy()
method that lets you perform a reduction operation on the values of each key. Notice that the type of
the second argument is
Collector
. The
Collectors
class contains many methods that return a
Collector
that you
will be using as the second argument.
Let's try a simple case where you want to group people by gender and count the number of people in each group.
The
counting()
method of the
Collectors
class returns a
Collector
to count the number of elements in a stream.
The following snippet of code accomplishes this:
Map<Person.Gender, Long> countByGender =
Person.persons()
.stream()
.collect(Collectors.groupingBy(Person::getGender, Collectors.counting()));
System.out.println(countByGender);
{MALE=4, FEMALE=2}
Let's get back to the example of listing a person's name by gender. You need to use the
mapping()
method of the
Collectors
class to get a collector that will map the list of people in the value of a key to their names and join them.
The signature of the
mapping()
method is
mapping(Function<? super T,? extends U> mapper, Collector<?
super U,A,R> downstream)
Notice the type of the second argument of the
mapping()
method. It is another
Collector
. This is where dealing
with grouping data gets complex. You need to nest collectors inside collectors. To simplify the grouping process, you
break down the things you want to perform on the data. You have already grouped people by their gender. The value
of the each key in the map was a
List<Person>
. Now you want to reduce the
List<Person>
to a
String
that contains
a comma-separated list of all people in the list. You need to think about this operation separately to avoid confusion.
You can accomplish this reduction as follows:
•
•
Use a function to map each person to his/her name. This function could be as simple as a
method reference like
Person::getName
. Think of the output of this step as a stream of person
names in a group.
•
What do you want to do with the stream of names generated in the first step? You may want to
collect them in a
String
, a
List
, a
Set
, or some other data structure. In this case, you want to
join the names of people, so you will use the collector returned from the
joining()
method of
the
Collectors
class.
The following snippet of code shows how to group the names of person by gender:
Map<Person.Gender, String> namesByGender =
Person.persons()
.stream()
.collect(Collectors.groupingBy(Person::getGender,
Collectors.mapping(Person::getName, Collectors.joining(", "))));
System.out.println(namesByGender);
{MALE=Ken, Jeff, Chris, Li, FEMALE=Donna, Laynie}