Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Grouping Maps by Key Using Java Streams and toMap

Tech May 10 2

Stream operations in Java are frequent employed to reduce code volume and improve readability, provided the performance impact is acceptable for the given use case. While using streams on very large collections requires caution, they are well-suited for non-critical business logic.

A common requirement is to transform a List<Map<String, Object>> into a Map<String, List<Map<String, Object>>>, grouping elements by a specific key from the inner maps. This approach is preferable to repeated list traversal, as it creates more maintainable and understandable code. Key-based lookups are generally more intuitive then positional indices.

Consider the following example where a list of student records needs to be grouped by thier class:

import java.util.*;
import java.util.stream.Collectors;

public class GroupingExample {
    public static void main(String[] args) {
        List<Map<String, Object>> studentRecords = new ArrayList<>();

        // Creating student record maps
        Map<String, Object> record1 = createRecord("fujian", "a1", "male");
        Map<String, Object> record2 = createRecord("fujian", "a2", "female");
        Map<String, Object> record3 = createRecord("beijing", "b1", "male");
        Map<String, Object> record4 = createRecord("beijing", "b2", "female");
        Map<String, Object> record5 = createRecord("wulumuqi", "c1", "female");
        Map<String, Object> record6 = createRecord("wulumuqi", "c2", "male");

        Collections.addAll(studentRecords, record1, record2, record3, record4, record5, record6);

        // Grouping records by the 'className' key
        Map<String, List<Map<String, Object>>> groupedByClass = studentRecords.stream()
                .collect(Collectors.toMap(
                        entry -> entry.get("className").toString(),
                        entry -> {
                            List<Map<String, Object>> initialList = new ArrayList<>();
                            Map<String, Object> simplifiedRecord = new HashMap<>();
                            simplifiedRecord.put("name", entry.get("name"));
                            simplifiedRecord.put("sex", entry.get("sex"));
                            initialList.add(simplifiedRecord);
                            return initialList;
                        },
                        (existingList, additionalList) -> {
                            existingList.addAll(additionalList);
                            return existingList;
                        }
                ));

        System.out.println(groupedByClass);
    }

    private static Map<String, Object> createRecord(String className, String name, String sex) {
        Map<String, Object> record = new HashMap<>();
        record.put("className", className);
        record.put("name", name);
        record.put("sex", sex);
        return record;
    }
}

This code produces a map where the keys are class names (e.g., "fujian", "beijing", "wulumuqi") and the values are lists containing simplified student maps (with only "name" and "sex" attributes). The merge function (existingList, additionalList) -> {...} handles cases where multiple records share the same class name by combining their lists.

Input Data (List of Maps):

[
    {"name":"a1", "className":"fujian", "sex":"male"},
    {"name":"a2", "className":"fujian", "sex":"female"},
    {"name":"b1", "className":"beijing", "sex":"male"},
    {"name":"b2", "className":"beijing", "sex":"female"},
    {"name":"c1", "className":"wulumuqi", "sex":"female"},
    {"name":"c2", "className":"wulumuqi", "sex":"male"}
]

Output Map (Grouped by Class):

{
  "wulumuqi": [
    {"name": "c1", "sex": "female"},
    {"name": "c2", "sex": "male"}
  ],
  "beijing": [
    {"name": "b1", "sex": "male"},
    {"name": "b2", "sex": "female"}
  ],
  "fujian": [
    {"name": "a1", "sex": "male"},
    {"name": "a2", "sex": "female"}
  ]
}

While the Collectors.toMap method is powerful for custom grouping and merging logic, alternative approaches like Collectors.groupingBy may offer simpler syntax for standard grouping operations. The performance characteristics of different stream-based and iterative solutions vary and should be considered based on data size and context.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.