Flattening Nested Map Results with Java Streams

📘 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

When working with Java Streams, especially with Collectors.groupingBy(), it's common to end up with nested Map<K, Map<K2, V>> structures. While these are useful for grouping, they can quickly become unwieldy when you need to flatten the data for reporting, filtering, or transformation.

In this article, you'll learn:

  • 🧱 How nested maps are created using groupingBy()
  • 🔁 How to flatten them using Streams
  • 📦 Real-world examples with product and sales data
  • 🧠 Best practices for working with flattened results

🧪 Sample Domain

Let’s define a simple domain for our examples:

record Product(String name, String category, String region, double sales) {}
List<Product> products = List.of(
    new Product("iPhone", "Electronics", "Asia", 50000),
    new Product("TV", "Electronics", "Asia", 40000),
    new Product("Laptop", "Electronics", "US", 120000),
    new Product("Shirt", "Clothing", "US", 2500),
    new Product("Jeans", "Clothing", "Asia", 3000)
);

🧱 1. Grouping Products by Category and Region

Let’s say you want to group products by category and then by region:

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

This produces a structure like:

{
  Electronics = {
      Asia = [iPhone, TV],
      US = [Laptop]
  },
  Clothing = {
      Asia = [Jeans],
      US = [Shirt]
  }
}

✅ 2. Flattening Nested Maps into Flat List of Entries

🎯 Goal: Convert to a flat structure like:

List<String> => "Category: Electronics, Region: Asia, Products: 2"

💡 Solution:

List<String> flatList = nestedMap.entrySet().stream()
    .flatMap(categoryEntry -> 
        categoryEntry.getValue().entrySet().stream()
            .map(regionEntry -> {
                String category = categoryEntry.getKey();
                String region = regionEntry.getKey();
                int count = regionEntry.getValue().size();
                return "Category: " + category + ", Region: " + region + ", Products: " + count;
            })
    )
    .toList();

flatList.forEach(System.out::println);

🧾 Output:

Category: Electronics, Region: Asia, Products: 2  
Category: Electronics, Region: US, Products: 1  
Category: Clothing, Region: Asia, Products: 1  
Category: Clothing, Region: US, Products: 1

✅ 3. Flatten and Aggregate — Total Sales per Category-Region

What if you want to calculate total sales instead of just flattening?

List<String> flatSales = nestedMap.entrySet().stream()
    .flatMap(categoryEntry -> 
        categoryEntry.getValue().entrySet().stream()
            .map(regionEntry -> {
                String category = categoryEntry.getKey();
                String region = regionEntry.getKey();
                double totalSales = regionEntry.getValue().stream()
                    .mapToDouble(Product::sales)
                    .sum();
                return "Category: " + category + ", Region: " + region + ", Sales: " + totalSales;
            })
    )
    .toList();

flatSales.forEach(System.out::println);

🧾 Output:

Category: Electronics, Region: Asia, Sales: 90000.0  
Category: Electronics, Region: US, Sales: 120000.0  
Category: Clothing, Region: Asia, Sales: 3000.0  
Category: Clothing, Region: US, Sales: 2500.0

✅ 4. Returning as Map<String, Double> (Flat Map Key)

You might prefer returning a map with composite keys like "Electronics-Asia":

Map<String, Double> salesByCategoryRegion = nestedMap.entrySet().stream()
    .flatMap(categoryEntry ->
        categoryEntry.getValue().entrySet().stream()
            .map(regionEntry -> {
                String key = categoryEntry.getKey() + "-" + regionEntry.getKey();
                double totalSales = regionEntry.getValue().stream()
                    .mapToDouble(Product::sales)
                    .sum();
                return Map.entry(key, totalSales);
            })
    )
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

System.out.println(salesByCategoryRegion);

🧾 Output:

{Electronics-Asia=90000.0, Electronics-US=120000.0, Clothing-Asia=3000.0, Clothing-US=2500.0}

🧠 Best Practices for Flattening Nested Maps

Practice Benefit
Use flatMap() Unroll nested structures easily
Use Map.Entry Clean key-value pair creation
Combine with Collectors.toMap() Return flat maps
Stream internal values Aggregate lists in-place

📌 Summary

Use Case Stream Pattern
Flatten nested map .flatMap(map -> innerMap.stream())
Return a flat list Use toList()
Return a flat map Use Collectors.toMap()
Aggregate values Use mapToX().sum() or downstream collectors

✅ Final Thoughts

Flattening nested map results with Java Streams allows you to:

  • Extract clean reports
  • Reshape complex aggregations
  • Make downstream processing easier

Think of flatMap() as a "flatten + map" combo — and pair it with collectors to produce readable, powerful stream transformations.

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