VOOZH about

URL: https://www.javacodegeeks.com/2016/03/multi-level-grouping-streams.html

⇱ Multi level grouping with streams - Java Code Geeks


1. Introduction

With Java 8 streams it is pretty easy to group collections of objects based on different criteria. In this post, we will see how we can make from simple single level groupings to more complex, involving several levels of groupings.

We will use two classes to represent the objects we want to group by: person and pet.

Person.class

public class Person {
 private final String name;
 private final String country;
 private final String city;
 private final Pet pet;
 
 public Person(String name, String country, String city, Pet pet) {
 this.name = name;
 this.country = country;
 this.city = city;
 this.pet = pet;
 }
 
 public String getName() {
 return name;
 }
 
 public String getCountry() {
 return country;
 }
 
 public String getCity() {
 return city;
 }
 
 public Pet getPet() {
 return pet;
 }
 
 @Override
 public String toString() {
 return "Person{" +
 "name='" + name + '\'' +
 ", country='" + country + '\'' +
 ", city='" + city + '\'' +
 '}';
 }
}

Pet.class

public class Pet {
 private final String name;
 private final int age;
 
 public Pet(String name, int age) {
 this.name = name;
 this.age = age;
 }
 
 public String getName() {
 return name;
 }
 
 public int getAge() {
 return age;
 }
 
 @Override
 public String toString() {
 return "Pet{" +
 "name='" + name + '\'' +
 ", age=" + age +
 '}';
 }
}

In the main method we create the collection we will use in the following sections.

public static void main(String[] args) {
 Person person1 = new Person("John", "USA", "NYC", new Pet("Max", 5));
 Person person2 = new Person("Steve", "UK", "London", new Pet("Lucy", 8));
 Person person3 = new Person("Anna", "USA", "NYC", new Pet("Buddy", 12));
 Person person4 = new Person("Mike", "USA", "Chicago", new Pet("Duke", 10));
 
 List<Person> persons = Arrays.asList(person1, person2, person3, person4);
  • You can take a look at the source code here.

2. Single level grouping

The simplest form of grouping is the single level grouping. In this example we are going to group all persons in the collection by their country:

public void singleLevelGrouping(List<Person> persons) {
 final Map<String, List<Person>> personsByCountry = persons.stream().collect(groupingBy(Person::getCountry));
 
 System.out.println("Persons in USA: " + personsByCountry.get("USA"));
}

If we take a look into the map, we can see how each country contains a list of its citizens:

πŸ‘ singleGroup

The result shows persons living in the specified country:

Persons in USA: [Person{name='John', country='USA', city='New York'}, Person{name='Anna', country='USA', city='New York'}, Person{name='Mike', country='USA', city='Chicago'}]

3. Two level grouping

In this example, we will group not only by country but also by city. To accomplish this, we need to implement a two level grouping. We will group persons by country and for each country, we will group its persons by the city where they live.

In order to allow multi level grouping, the groupingBy method in class Collectors supports an additional Collector as a second argument:

 public static <T, K, A, D>
 Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
 Collector<? super T, A, D> downstream)

Let’s use this method to implement our two level grouping:

 public void twoLevelGrouping(List<Person> persons) {
 final Map<String, Map<String, List<Person>>> personsByCountryAndCity = persons.stream().collect(
 groupingBy(Person::getCountry,
 groupingBy(Person::getCity)
 )
 );
 System.out.println("Persons living in London: " + personsByCountryAndCity.get("UK").get("London").size());
}

If we debug the execution, we will see how people is distributed:

πŸ‘ twoLevelGrouping

4. Three level grouping

In our final example, we will take a step further and group people by country, city and pet name. I have splitted it into two methods for readability:

public void threeLevelGrouping(List<Person> persons) {
 final Map<String, Map<String, Map<String, List<Person>>>> personsByCountryCityAndPetName = persons.stream().collect(
 groupingBy(Person::getCountry,
 groupByCityAndPetName()
 )
 );
 System.out.println("Persons whose pet is named 'Max' and live in NY: " +
 personsByCountryCityAndPetName.get("USA").get("NYC").get("Max").size());
}

private Collector<Person, ?, Map<String, Map<String, List<Person>>>> groupByCityAndPetName() {
 return groupingBy(Person::getCity, groupingBy(p -> p.getPet().getName()));
}

Now we have three nested maps containing each list of persons:

πŸ‘ threeLevelGrouping

5. Conclusion

The Java 8 Collectors API provides us with an easy way to group our collections. By nesting collectors, we can add different layers of groups to implement multi level groupings.

Reference: Multi level grouping with streams from our JCG partner Xavier Padro at the Xavier Padró’s Blog blog.
Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

Thank you!

We will contact you soon.

πŸ‘ Photo of Xavier Padro
Xavier Padro
March 1st, 2016Last Updated: March 1st, 2016
6 3,750 3 minutes read

Xavier Padro

Xavier is a software developer working in a consulting firm based in Barcelona. He is specialized in web application development with experience in both frontend and backend. He is interested in everything related to Java and the Spring framework.
Subscribe

This site uses Akismet to reduce spam. Learn how your comment data is processed.

6 Comments
Oldest
Newest Most Voted
Thanh
10 years ago

Excellent tutorial!

0
Reply
Alexander H.
8 years ago

Great tutorial! Thanks a lot!

0
Reply
Alex
7 years ago

How to create grouping if the number of arguments can be variable?

0
Reply
Andrea
7 years ago

many thanks, clear and proficient!

0
Reply
invite
5 years ago

Where is the output ?

0
Reply
Rahul Kumar
4 years ago

public static void twoLevelGrouping(List<Person> persons) {
   final Map<String, Map<String, List<Person>>> personsByCountryAndCity = persons.stream().collect(
     groupingBy(Person::getCountry,
      groupingBy(Person::getCity)
    )
  );
  System.out.println(β€œPersons living in London: ” + personsByCountryAndCity.get(β€œUK”).get(β€œLondon”).size());
}

–> Here, groupingBy(Person::getCity) giving red line error….. Why ?

Last edited 4 years ago by Rahul Kumar
2
Reply
Back to top button
Close
wpDiscuz