
在Java开发中,我们经常需要对集合数据进行转换和聚合。一个常见的需求是将一个对象列表根据某个属性进行分组,并将分组结果存储到Map中,其中Map的键是分组依据的属性值,而值是所有符合该属性的对象列表。例如,我们可能有一个Child对象的列表,每个Child对象都关联一个Parent,我们希望将所有Child对象按照其parent_id进行分组。
假设我们有以下数据结构:
// 父级实体
class Parent {
private Long parentId;
private String projectDesc;
public Parent(Long parentId, String projectDesc) {
this.parentId = parentId;
this.projectDesc = projectDesc;
}
public Long getParentId() {
return parentId;
}
// Getters and other methods
}
// 子级实体
class Child {
private Long childId;
private Parent parent; // 子级关联父级对象
private String code;
public Child(Long childId, Parent parent, String code) {
this.childId = childId;
this.parent = parent;
this.code = code;
}
public Long getChildId() {
return childId;
}
public Parent getParent() {
return parent;
}
public String getCode() {
return code;
}
@Override
public String toString() {
return "Child{" +
"childId=" + childId +
", parentId=" + (parent != null ? parent.getParentId() : "null") +
", code='" + code + '\'' +
'}';
}
}现在,我们有一个List<Child>,包含以下示例数据:
Parent parent1 = new Parent(1L, "One");
Parent parent2 = new Parent(2L, "Two");
List<Child> childEntityList = Arrays.asList(
new Child(1L, parent1, "code1"),
new Child(2L, parent1, "code2"),
new Child(3L, parent1, "code3"),
new Child(4L, parent2, "code4"),
new Child(5L, parent2, "code5")
);我们的目标是将其转换为Map<Long, List<Child>>,其中键是parent_id,值是所有属于该parent_id的Child对象列表。
立即学习“Java免费学习笔记(深入)”;
初学者在尝试使用Java Stream API实现此功能时,可能会倾向于使用Collectors.toMap(),如下所示:
import java.util.List; import java.util.Map; import java.util.stream.Collectors; // ... (Child and Parent class definitions as above) // 错误示例:使用 toMap() // Map<Long, List<Child>> childEntityMap = childEntityList.stream() // .collect(Collectors.toMap( // childEntity -> childEntity.getParent().getParentId(), // childEntity -> childEntity // ));
上述代码在运行时会抛出IllegalStateException: Duplicate key ...异常。这是因为Collectors.toMap()默认期望每个键都是唯一的。当多个Child对象拥有相同的parent_id时(例如,child1、child2和child3都属于parent1),toMap()无法处理同一个键关联多个值的情况,从而导致异常。
为了解决一对多(one-to-many)的映射问题,Java Stream API提供了专门的收集器:Collectors.groupingBy()。这个收集器正是为这种分组场景设计的。
Collectors.groupingBy()的基本用法是接收一个分类函数(classifier function),该函数用于从流中的每个元素中提取出作为Map键的值。默认情况下,所有被分类到同一个键的元素将被收集到一个List中作为Map的值。
以下是使用Collectors.groupingBy()正确实现上述需求的示例代码:
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Arrays; // For Arrays.asList
public class StreamGroupingExample {
// ... (Child and Parent class definitions as above)
public static void main(String[] args) {
Parent parent1 = new Parent(1L, "One");
Parent parent2 = new Parent(2L, "Two");
List<Child> childEntityList = Arrays.asList(
new Child(1L, parent1, "code1"),
new Child(2L, parent1, "code2"),
new Child(3L, parent1, "code3"),
new Child(4L, parent2, "code4"),
new Child(5L, parent2, "code5")
);
// 正确示例:使用 groupingBy()
Map<Long, List<Child>> childEntityMap = childEntityList.stream()
.collect(Collectors.groupingBy(
childEntity -> childEntity.getParent().getParentId()
));
// 打印结果以验证
childEntityMap.forEach((parentId, children) -> {
System.out.println("Parent ID: " + parentId);
children.forEach(child -> System.out.println(" " + child));
});
/*
预期输出:
Parent ID: 1
Child{childId=1, parentId=1, code='code1'}
Child{childId=2, parentId=1, code='code2'}
Child{childId=3, parentId=1, code='code3'}
Parent ID: 2
Child{childId=4, parentId=2, code='code4'}
Child{childId=5, parentId=2, code='code5'}
*/
}
}在上述代码中:
通过本文的介绍,您应该已经掌握了如何使用Collectors.groupingBy()来高效地将对象列表按照指定属性分组到Map中,从而避免了toMap()在处理一对多关系时可能遇到的问题。掌握这一技巧对于编写高效、健壮的Java Stream代码至关重要。
以上就是使用Java Stream API高效实现对象列表按键分组到Map的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号