Grouping Maps by Key Using Java Streams and toMap
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.