我有两个数组映射。
Map<String, List<String>> map1 = new HashMap<>();
Map<String, List<String>> map2 = new HashMap<>();
我想将它们合并到一张新地图中 如果两个映射中都存在密钥,那么我应该合并数组。
例如:
map1.put("k1", Arrays.asList("a0", "a1"));
map1.put("k2", Arrays.asList("b0", "b1"));
map2.put("k2", Arrays.asList("z1", "z2"));
// Expected output is
Map 3: {k1=[a0, a1], k2=[b0, b1, z1, z2]}
我尝试用流
做到这一点Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().stream().collect(Collectors.toList())
));
如果地图中没有相同的键,则此工作。否则,我得到例外
Exception in thread "main" java.lang.IllegalStateException: Duplicate key k2 (attempted merging values [b0, b1] and [z1, z2])
at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:133)
at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:180)
at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.base/java.util.HashMap$EntrySpliterator.forEachRemaining(HashMap.java:1751)
at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:274)
at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at im.djm.Test.main(Test.java:25)
有没有办法用流完成这项任务?
或者我必须迭代地图?
答案 0 :(得分:10)
在重复键的情况下使用合并功能:
Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> new ArrayList<>(e.getValue()),
(left, right) -> {left.addAll(right); return left;}
));
注意,我已将e -> e.getValue().stream().collect(Collectors.toList())
更改为new ArrayList<>(e.getValue())
,以确保我们始终拥有一个可变列表,我们可以在合并函数中添加该列表。
答案 1 :(得分:5)
也许。但是,通过使用迭代手动组合条目,您更有可能使一切正确。我不知道是否还有其他人需要处理这段代码,但他们可能会对易于阅读的方法表示感谢。
答案 2 :(得分:4)
你也可以这样做:
Map<String, List<String>> map3 = Stream.concat(map1.entrySet().stream(),
map2.entrySet().stream())
.collect(Collectors.groupingBy(Entry::getKey,
Collectors.mapping(Entry::getValue,
Collectors.flatMapping(List::stream,
Collectors.toList()))));
答案 3 :(得分:2)
您必须使用允许合并重复键的重载toMap()
版本:
toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction)
你可以写一些东西:
Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> new ArrayList<>(e.getValue()),
(e1, e2) -> { e1.addAll(e2); return e1;}
));
答案 4 :(得分:2)
Map<String, List<String>> map1 = new HashMap<>();
Map<String, List<String>> map2 = new HashMap<>();
map1.put("k1", Arrays.asList("a0", "a1"));
map1.put("k2", Arrays.asList("b0", "b1"));
map2.put("k2", Arrays.asList("z1", "z2"));
Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(p -> p.entrySet().stream())
.flatMap(p -> p.getValue().stream().map(q -> new Pair<>(p.getKey(), q)))
.collect(
Collectors.groupingBy(
p -> p.getKey(),
Collectors.mapping(p -> p.getValue(), Collectors.toList())
)
);
这样的工作原理如下:
Stream<Map<String,List<String>>>
Entry<String, List<String>>
Pair<String, String>
答案 5 :(得分:0)
以下是使用两个地图的迭代的示例。第一次迭代将来自map1和map2的公共键/值对连接在一起,并将它们添加到结果映射中,或者将map1中的唯一键/值对添加到结果映射中。第二次迭代会抓取map2中与map1不匹配的任何剩余内容,并将它们添加到生成的地图中。
public static Map<String, ArrayList<String>> joinMaps(Map<String, ArrayList<String>> map1, Map<String, ArrayList<String>> map2)
{
Map<String, ArrayList<String>> mapJoined = new HashMap<>();
//join values from map2 into values of map1 or add unique key/values of map1
for (Map.Entry<String, ArrayList<String>> entry : map1.entrySet()) {
String key = entry.getKey();
ArrayList<String> value = entry.getValue();
if(map2.containsKey(key))
{
value.addAll(map2.get(key));
mapJoined.put(key, value);
}
else
mapJoined.put(key, value);
}
//add the non-duplicates left over in map 2
for (Map.Entry<String, ArrayList<String>> entry : map2.entrySet()) {
if(!mapJoined.containsKey(entry.getKey()))
mapJoined.put(entry.getKey(), entry.getValue());
}
return mapJoined;
}
您还可以在函数中添加一个Set以跟踪第一次迭代中添加的所有键,然后如果Set的大小== map2的大小,您知道这些映射具有相同的键并且不需要迭代第二张地图map2。
答案 6 :(得分:0)
其他方式就是这样。
你应该使用更大的地图初始化map3
。(这里是map1
)。然后在其他地图上使用循环,并使用merge
方法组合重复键。
Map<String, List<String>> map3 = new HashMap<>(map1);
for (Map.Entry<String, List<String>> entry : map2.entrySet()) {
List<String> values = new ArrayList<>(entry.getValue());
map3.merge(entry.getKey(),entry.getValue(),(l1, l2) -> {values.addAll(l1);
return values;
});
}
map2.forEach((key, value) -> {
List<String> values = new ArrayList<>(value);
map3.merge(key,value, (l1, l2) -> {values.addAll(l1);return values;});
});
答案 7 :(得分:0)
这是另一种合并地图和列表的方法。
Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> Stream.concat(e1.stream(), e2.stream()).collect(Collectors.toList())
));
toMap
方法中的第三个参数是
(e1, e2) -> Stream.concat(e1.stream(), e2.stream()).collect(Collectors.toList())
是mergeFunction
功能
此功能适用于重复项。
如果映射的键包含重复项(根据
Object.equals(Object))
,则值映射函数将应用于每个相等的元素,并使用提供的合并函数合并结果。
JavaDoc