Java 8 – Grouping Elements Using Stream Collectors

Introduction

Java 8 introduced the Stream API, which enables developers to perform complex data processing operations in a more declarative and readable way. One of the most powerful features of the Stream API is the ability to group elements in a collection using Collectors.groupingBy(). Grouping allows you to organize data into subgroups based on a classifier function, making it easier to analyze or manipulate the data.

In this guide, we will explore how to group elements in a stream using Collectors.groupingBy(). We’ll look at different scenarios, including simple grouping by a single field and more complex groupings with multiple levels.

Table of Contents

  • Problem Statement
  • Solution Steps
  • Java Program
    • Basic Grouping by a Single Field
    • Grouping by Multiple Fields
    • Grouping and Counting Elements
  • Advanced Considerations
  • Conclusion

Problem Statement

The task is to create a Java program that:

  • Groups elements in a collection based on a specific criterion.
  • Uses the Collectors.groupingBy() method to perform the grouping.
  • Outputs the grouped elements.

Example 1:

  • Input: List of Product objects, where each product has a category and price.
  • Output: Map of products grouped by category.

Example 2:

  • Input: List of Product objects, grouped by both category and price range.
  • Output: Nested map of products grouped by category and then by price range.

Solution Steps

  1. Create a Stream: Start with a stream of elements that you want to group.
  2. Group the Elements Using Collectors.groupingBy(): Apply the grouping function based on the desired criterion.
  3. Display the Result: Print the grouped elements or process them further.

Java Program

Basic Grouping by a Single Field

A common use case for Collectors.groupingBy() is grouping objects by a single field. For example, you might want to group products by their category.

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Java 8 - Basic Grouping by a Single Field
 * Author: https://www.rameshfadatare.com/
 */
public class GroupingBySingleField {

    public static void main(String[] args) {
        // Step 1: Create a list of products
        List<Product> products = List.of(
            new Product("Laptop", "Electronics", 1500),
            new Product("Phone", "Electronics", 800),
            new Product("Tablet", "Electronics", 600),
            new Product("Sofa", "Furniture", 1200),
            new Product("Chair", "Furniture", 300)
        );

        // Step 2: Group products by category
        Map<String, List<Product>> productsByCategory = products.stream()
            .collect(Collectors.groupingBy(Product::getCategory));

        // Step 3: Display the result
        productsByCategory.forEach((category, productList) -> {
            System.out.println("Category: " + category);
            productList.forEach(product -> 
                System.out.println("  Product: " + product.getName() + ", Price: " + product.getPrice()));
        });
    }
}

// Custom class Product
class Product {
    private String name;
    private String category;
    private double price;

    public Product(String name, String category, double price) {
        this.name = name;
        this.category = category;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public String getCategory() {
        return category;
    }

    public double getPrice() {
        return price;
    }
}

Output

Category: Electronics
  Product: Laptop, Price: 1500.0
  Product: Phone, Price: 800.0
  Product: Tablet, Price: 600.0
Category: Furniture
  Product: Sofa, Price: 1200.0
  Product: Chair, Price: 300.0

Explanation

  • The products.stream() method creates a stream of Product objects.
  • The collect(Collectors.groupingBy(Product::getCategory)) method groups the products by their category.
  • The grouped products are stored in a map where the key is the category and the value is the list of products in that category.

Grouping by Multiple Fields

You can also group elements by multiple fields, such as grouping by category and then by price range.

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Java 8 - Grouping by Multiple Fields
 * Author: https://www.rameshfadatare.com/
 */
public class GroupingByMultipleFields {

    public static void main(String[] args) {
        // Step 1: Create a list of products
        List<Product> products = List.of(
            new Product("Laptop", "Electronics", 1500),
            new Product("Phone", "Electronics", 800),
            new Product("Tablet", "Electronics", 600),
            new Product("Sofa", "Furniture", 1200),
            new Product("Chair", "Furniture", 300)
        );

        // Step 2: Group products by category and then by price range
        Map<String, Map<String, List<Product>>> productsByCategoryAndPrice = products.stream()
            .collect(Collectors.groupingBy(
                Product::getCategory,
                Collectors.groupingBy(product -> {
                    if (product.getPrice() < 500) return "Budget";
                    else if (product.getPrice() <= 1000) return "Mid-Range";
                    else return "Premium";
                })
            ));

        // Step 3: Display the result
        productsByCategoryAndPrice.forEach((category, priceMap) -> {
            System.out.println("Category: " + category);
            priceMap.forEach((priceRange, productList) -> {
                System.out.println("  Price Range: " + priceRange);
                productList.forEach(product -> 
                    System.out.println("    Product: " + product.getName() + ", Price: " + product.getPrice()));
            });
        });
    }
}

Output

Category: Electronics
  Price Range: Premium
    Product: Laptop, Price: 1500.0
  Price Range: Mid-Range
    Product: Phone, Price: 800.0
    Product: Tablet, Price: 600.0
Category: Furniture
  Price Range: Premium
    Product: Sofa, Price: 1200.0
  Price Range: Budget
    Product: Chair, Price: 300.0

Explanation

  • The groupingBy(Product::getCategory, Collectors.groupingBy(...)) method groups the products first by category and then by price range.
  • The nested map structure allows you to organize products by multiple criteria, making it easier to analyze the data.

Grouping and Counting Elements

In some cases, you may want to group elements and then count how many elements fall into each group.

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Java 8 - Grouping and Counting Elements
 * Author: https://www.rameshfadatare.com/
 */
public class GroupingAndCounting {

    public static void main(String[] args) {
        // Step 1: Create a list of products
        List<Product> products = List.of(
            new Product("Laptop", "Electronics", 1500),
            new Product("Phone", "Electronics", 800),
            new Product("Tablet", "Electronics", 600),
            new Product("Sofa", "Furniture", 1200),
            new Product("Chair", "Furniture", 300)
        );

        // Step 2: Group products by category and count them
        Map<String, Long> productCountByCategory = products.stream()
            .collect(Collectors.groupingBy(Product::getCategory, Collectors.counting()));

        // Step 3: Display the result
        productCountByCategory.forEach((category, count) -> 
            System.out.println("Category: " + category + ", Count: " + count));
    }
}

Output

Category: Electronics, Count: 3
Category: Furniture, Count: 2

Explanation

  • The collect(Collectors.groupingBy(Product::getCategory, Collectors.counting())) method groups the products by category and counts the number of products in each category.
  • The result is a map where the key is the category and the value is the count of products in that category.

Advanced Considerations

  • Custom Collectors: You can create custom collectors to perform more complex operations during grouping, such as calculating averages or summing values.

  • Immutable Collections: If you need the resulting maps to be immutable, consider using Collectors.toUnmodifiableMap() or wrapping the map with Collections.unmodifiableMap().

  • Performance Considerations: Grouping operations can be resource-intensive, especially with large datasets. Ensure your application’s performance requirements are met, and consider using parallel streams if appropriate.

Conclusion

This guide provides methods for grouping elements in Java 8 using the Collectors.groupingBy() method, covering basic grouping, grouping by multiple fields, and grouping with counting. Grouping elements is a powerful way to organize and analyze data, making it easier to perform operations based on specific criteria. By mastering the use of Collectors.groupingBy(), you can handle complex data processing tasks more effectively in your Java applications.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top