lcardito lcardito - 1 year ago 61
Java Question

Java 8 grouping using custom collector?

I have the following class.

class Person {

String name;
LocalDate birthday;
Sex gender;
String emailAddress;

public int getAge() {
return birthday.until(IsoChronology.INSTANCE.dateNow()).getYears();

public String getName() {
return name;

I'd like to be able to group by age and then collect the list of the persons names rather than the Person object itself; all in a single nice lamba expression.

To simplify all of this I am linking my current solution that store the result of the grouping by age and then iterates over it to collect the names.

ArrayList<OtherPerson> members = new ArrayList<>();

members.add(new OtherPerson("Fred",, 6, 20), OtherPerson.Sex.MALE, ""));
members.add(new OtherPerson("Jane",, 7, 15), OtherPerson.Sex.FEMALE, ""));
members.add(new OtherPerson("Mark",, 7, 15), OtherPerson.Sex.MALE, ""));
members.add(new OtherPerson("George",, 8, 13), OtherPerson.Sex.MALE, ""));
members.add(new OtherPerson("Bob",, 9, 12), OtherPerson.Sex.MALE, ""));

Map<Integer, List<Person>> collect =;

Map<Integer, List<String>> result = new HashMap<>();

collect.keySet().forEach(key -> {
result.put(key, collect.get(key).stream().map(Person::getName).collect(toList()));

Current solution

Not ideal and for the sake of learning I'd like to have a more elegant and performing solution.

Answer Source

When grouping a Stream with Collectors.groupingBy, you can specify a reduction operation on the values with a custom Collector. Here, we need to use Collectors.mapping, which takes a function (what the mapping is) and a collector (how to collect the mapped values). In this case the mapping is Person::getName, i.e. a method reference that returns the name of the Person, and we collect that into a List.

Map<Integer, List<String>> collect =
               Collectors.mapping(Person::getName, Collectors.toList()))