我想将对象列表转换为Map,其中Map的键和值位于List中Object内的属性。
此处转换的Java 7片段:
private Map<String, Child> getChildren(List<Family> families ) {
Map<String, Child> convertedMap = new HashMap<String, Child>();
for (Family family : families) {
convertedMap.put(family.getId(), family.getParent().getChild());
}
return convertedMap;
}
答案 0 :(得分:11)
它应该类似于......
Map<String, Child> m = families.stream()
.collect(Collectors.toMap(Family::getId, f -> f.getParent().getChild()));
答案 1 :(得分:9)
杰森给了decent answer(+1),但我应该指出它与OP的Java 7代码有不同的语义。如果输入列表中的两个族实例具有重复的ID,则该问题与行为有关。也许他们保证独一无二,在这种情况下没有区别。但是,如果使用OP的原始代码存在重复项,则列表中稍后的Family
将覆盖列表中具有相同ID的Family
的映射条目。< / p>
使用Jason的代码(如下所示,略有修改):
Map<String, Child> getChildren(List<Family> families) {
return families.stream()
.collect(Collectors.toMap(Family::getId, f -> f.getParent().getChild()));
}
如果有任何重复的密钥,Collectors.toMap
操作将抛出IllegalStateException
。这有点不愉快,但至少它会通知您存在重复,而不是可能会无声地丢失数据。 Collectors.toMap(keyMapper, valueMapper)
的规则是,您需要确保键映射器函数为流的每个元素返回唯一键。
您需要做些什么 - 如果有的话 - 取决于问题域。一种可能性是使用三个arg版本:Collectors.toMap(keyMapper, valueMapper, mergeFunction)
。这指定了在重复的情况下调用的额外函数。如果您希望以后的条目覆盖之前的条目(与原始Java 7代码匹配),您可以这样做:
Map<String, Child> getChildren(List<Family> families) {
return families.stream()
.collect(Collectors.toMap(Family::getId, f -> f.getParent().getChild(),
(child1, child2) -> child2));
}
另一种方法是为每个家庭建立一个儿童名单,而不是只有一个孩子。您可以编写一个更复杂的合并函数,该函数为第一个子项创建一个列表,并为第二个和后续子项附加到此列表中。这很常见,有一个特殊的groupingBy
收集器可以自动执行此操作。这将产生按ID分组的家庭列表。我们不想要一个家庭列表,但我们想要一个子列表,所以我们添加一个下游映射操作来映射从一个家庭到另一个孩子,然后将孩子收集到一个列表中。代码如下所示:
Map<String, List<Child>> getChildren(List<Family> families) {
return families.stream()
.collect(Collectors.groupingBy(Family::getId,
Collectors.mapping(f -> f.getParent().getChild(),
Collectors.toList())));
}
请注意,返回类型已从Map<String, Child>
更改为Map<String, List<Child>>
。