Search code examples
javajava-streamgrouping

Java stream grouping by with custom value


I have a List<Person>. With Java streams I can group the persons by last name using something like this:

Map<String, List<Person>> persons.stream()
    .collect(Collectors.groupingBy(Person::getLastName));

But what if I want to group the first name of the persons by the last names? In other words, grouped by last name "Smith" I might have a list of "Jane" and "John". Is there maybe a value extractor version of groupingBy() I can use? I'm thinking something like this:

Map<String, List<String>> persons.stream()
    .collect(Collectors.groupingBy(Person::getLastName, Person::getFirstName));

Solution

  • Try use Collectors.mapping() as a downstream collector within the Collectors.groupingBy() method:

    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    
    class Main {
        public static void main(String[] args) {
            List<Person> persons = List.of(
                new Person("Smith", "John"),
                new Person("Doe", "Jane"),
                new Person("Smith", "Alex"),
                new Person("Doe", "John"),
                new Person("Brown", "Charlie")
            );
            Map<String, List<String>> groupedByLastName = persons.stream()
                .collect(Collectors.groupingBy(
                    Person::getLastName,
                    Collectors.mapping(Person::getFirstName, Collectors.toList())
                ));
            System.out.println(groupedByLastName);
        }
    }
    
    class Person {
        private String lastName;
        private String firstName;
    
        public Person(String lastName, String firstName) {
            this.lastName = lastName;
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        @Override
        public String toString() {
            return "Person{lastName='" + lastName + "', firstName='" + firstName + "'}";
        }
    }
    

    Output:

    {Brown=[Charlie], Smith=[John, Alex], Doe=[Jane, John]}