我有一组地图。内部地图可以包含匹配的键,因此我想将其转换为Map
Collection
:
Collection<Map<String, Thing>>
=&gt; Map<String, Collection<Thing>>
我尝试的是:
Map<String, Collection<Thing>> newMap = oldCollection
.stream()
.map(Map::entrySet)
.collect(Collectors.groupingBy(
Entry::getKey,
Collectors.mapping(Entry::getValue, Collectors.toList())));
我觉得这应该可行,但我收到编译错误:
Type mismatch: cannot convert from Map<Object,List<Object>> to Map<String,Collection<Thing>>
有谁知道我做错了什么?
答案 0 :(得分:3)
oldCollection.stream().map(Map::entrySet)
创建Stream<Set<Map.Entry<String,Thing>>>
,但您需要Stream<Map.Entry<String,Thing>>
。
因此您需要使用flatMap
:
Map<String, List<Thing>>
newMap = oldCollection.stream()
.flatMap(m->m.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
此外,由于您要将分组值映射到List
,因此输出类型应为Map<String, List<Thing>>
。
如果您想保留当前的输出类型,可以将Collectors.toList()
更改为Collectors.toCollection(ArrayList::new)
。
答案 1 :(得分:1)
或者你也可以这样做,
final Map<String, List<Thing>> thingsMap = oldCollection.stream()
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(
Map.Entry::getValue,
Collectors.toList()
)
));
你可以获得每个地图的入口集,你得到一组集合,然后你展平它们以从它们中获得一个大的流。最后你处理那个大流。
答案 2 :(得分:1)
为了比较,这是循环解决方案:
Map<String, Collection<Thing>> newMap = new HashMap<>();
for(Map<String, Thing> m: oldCollection)
m.forEach((s,t) -> newMap.computeIfAbsent(s, x->new ArrayList<>()).add(t));
您可以表达与Stream操作相同的逻辑:
Map<String, Collection<Thing>> newMap = oldCollection
.stream()
.collect(HashMap::new,
(r,m)->m.forEach((s,t)->r.computeIfAbsent(s,x->new ArrayList<>()).add(t)),
(r,m)->m.forEach((s,l)->r.computeIfAbsent(s,x->new ArrayList<>()).addAll(l)));
除了flatMap
的逻辑已集成到收集器中之外,其他解决方案也是如此。
答案 3 :(得分:0)
首先创建地图可能更简单:
Map<String, Collection<Thing>> map = new HashMap<>();
oldCollection.stream()
.map(Map::entrySet)
.flatMap(Set::stream)
.forEach(e -> map.computeIfAbsent(e.getKey(), k -> new ArrayList<>()).add(e.getValue()));