Real-World Grouping with Collectors.groupingBy() in Java

📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.

✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.

🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.

▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube

If you’ve worked with Java Streams, chances are you’ve heard of Collectors.groupingBy(). It’s one of the most powerful features for aggregating and organizing data — especially when you’re dealing with large datasets in real-world applications.

In this guide, you’ll learn:

  • ✅ What groupingBy() is and how it works
  • 📊 How to use it in real-world scenarios
  • 🔄 Advanced tricks like nested grouping and downstream collectors
  • ⚖️ When to use groupingBy() vs partitioningBy()

📦 What Is Collectors.groupingBy()?

Collectors.groupingBy() is a collector used with Java Streams to group elements of a collection by a classification function, returning a Map<K, List<T>> by default.

🔤 Basic Syntax:

Map<K, List<T>> map = stream.collect(Collectors.groupingBy(classifier));

🧱 Domain Model for Examples

We’ll use the following classes for our examples:

record Product(String name, String category, double price) {}

Sample data:

List<Product> products = List.of(
    new Product("iPhone", "Electronics", 90000),
    new Product("TV", "Electronics", 40000),
    new Product("Shirt", "Clothing", 1500),
    new Product("Jeans", "Clothing", 2000),
    new Product("Laptop", "Electronics", 120000)
);

✅ 1. Group Products by Category

Map<String, List<Product>> groupedByCategory = products.stream()
    .collect(Collectors.groupingBy(Product::category));

groupedByCategory.forEach((category, items) -> {
    System.out.println(category + ": " + items);
});

🧾 Output:

Electronics: [iPhone, TV, Laptop]
Clothing: [Shirt, Jeans]

✅ 2. Group and Count Products per Category

Use a downstream collector to count grouped items:

Map<String, Long> countByCategory = products.stream()
    .collect(Collectors.groupingBy(
        Product::category,
        Collectors.counting()
    ));

System.out.println(countByCategory);

🧾 Output:

Electronics: 3
Clothing: 2

✅ 3. Group by Category and Sum Prices

Map<String, Double> totalByCategory = products.stream()
    .collect(Collectors.groupingBy(
        Product::category,
        Collectors.summingDouble(Product::price)
    ));

System.out.println(totalByCategory);

🧾 Output:

Electronics: 250000.0
Clothing: 3500.0

✅ 4. Nested Grouping: Category → Price Range

Let’s group products first by category, then by price range.

Map<String, Map<String, List<Product>>> nestedGrouping = products.stream()
    .collect(Collectors.groupingBy(
        Product::category,
        Collectors.groupingBy(p -> {
            if (p.price() > 50000) return "Expensive";
            else return "Affordable";
        })
    ));

System.out.println(nestedGrouping);

🧾 Output:

Electronics: {
    Expensive=[iPhone, Laptop],
    Affordable=[TV]
}
Clothing: {
    Affordable=[Shirt, Jeans]
}

✅ 5. Grouping with Custom Collection (Set Instead of List)

Map<String, Set<String>> productNamesByCategory = products.stream()
    .collect(Collectors.groupingBy(
        Product::category,
        Collectors.mapping(Product::name, Collectors.toSet())
    ));

System.out.println(productNamesByCategory);

🧾 Output:

Electronics: [TV, Laptop, iPhone]
Clothing: [Shirt, Jeans]

✅ 6. Top Product per Category (Max by Price)

Map<String, Optional<Product>> mostExpensiveProduct = products.stream()
    .collect(Collectors.groupingBy(
        Product::category,
        Collectors.maxBy(Comparator.comparingDouble(Product::price))
    ));

System.out.println(mostExpensiveProduct);

🧾 Output:

Electronics: Laptop
Clothing: Jeans

✅ 7. Custom Key Grouping: First Letter of Product Name

Map<Character, List<Product>> byFirstLetter = products.stream()
    .collect(Collectors.groupingBy(p -> p.name().charAt(0)));

System.out.println(byFirstLetter);

🧾 Output:

i: [iPhone]
T: [TV]
S: [Shirt]
J: [Jeans]
L: [Laptop]

⚖️ groupingBy() vs partitioningBy()

Feature groupingBy() partitioningBy()
Keys Multiple (any type) Only two groups: true and false
Use case Group by any field/classifier Split data based on a boolean test
Return type Map<K, List<T>> Map<Boolean, List<T>>

🧪 Example:

Map<Boolean, List<Product>> expensivePartition = products.stream()
    .collect(Collectors.partitioningBy(p -> p.price() > 50000));

🧠 Final Thoughts

The Collectors.groupingBy() method is one of the most versatile tools in the Java Stream API. Whether you need to:

  • Group by one or more fields
  • Count, sum, or aggregate
  • Apply nested groupings or transformations

Start with basic groupings and then move into nested and custom downstream collectors as your use cases grow.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare