合并两个地图

我有两个映射,其键是String s,其值是Set 。 给定两个Map ,合并它们的最简单方法是什么,如果两个键相同,则值是两个集合的并集。 您可以假设值永远不为空,如果它有用,我们可以制作这些MapSortedMap

我们在谈论HashMap实例HashMap ? 在这种情况下,查找是O(1),因此您可以只取一个映射,迭代该映射的条目,查看其他映射是否包含该键。 如果没有,只需添加该集。 如果它包含密钥,则取两个集合的并集(通过将一个集合的所有元素添加到另一个集合)

为了说明一些代码,我在IDE中使用Set来自动完成

 Map> firstMap = new HashMap>( ); Map> secondMap = new HashMap>( ); Set>> entries = firstMap.entrySet(); for ( Map.Entry> entry : entries ) { Set secondMapValue = secondMap.get( entry.getKey() ); if ( secondMapValue == null ) { secondMap.put( entry.getKey(), entry.getValue() ); } else { secondMapValue.addAll( entry.getValue() ); } } 

您可以非常轻松地使用流执行此操作:

 Map> merged = Stream.of(first, second) .map(Map::entrySet) .flatMap(Set::stream) .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> { HashSet both = new HashSet<>(a); both.addAll(b); return both; })); 

这将映射拆分为其Entry ,然后将它们与Collector连接, Collector通过将两个值添加到新的HashSet来解析重复项 。

这也适用于任意数量的地图。

产生相同结果的一些变化:

 Stream.of(first, second).flatMap(m -> m.entrySet().stream()) .collect(...); Stream.concat(first.entrySet().stream(), second.entrySet().stream()) .collect(...); //from comment by Aleksandr Dubinsky 

如果没有重复键,则不需要Collectors.toMap的第三个参数。

还有另一个Collectors.toMap带有第四个参数,可让您决定收集到的Map的类型。

这个怎么样(未经测试):

 Map> m1 = // input map Map> m2 = // input map Map> ret = // new empty map ret.putAll(m1); for(String key : m2.keySet()) { if(ret.containsKey(key)) { ret.get(key).addAll(m2.get(key)); } else { ret.put(key,m2.get(key)); } } 

此解决方案不会修改输入映射,并且因为它很短并且仅依赖于API方法,所以我发现它非常易读。

请注意, putAll()addAll()都是MapSet可选方法。 因此(为了获得O(1)查找),我建议使用HashMapHashSet

请注意,因为HashSetHashMap都不是同步的,所以如果您需要线程安全的代码,则需要寻找其他解决方案。

 static void mergeSet(Map> map1, Map> map2) { map1.forEach((key1, value1) -> { map2.merge(key1, value1, (key2, value2) -> key2).addAll(value1); }); } 

以下应将map1合并到map2 (未经测试):

 for (Entry> entry : map1.entrySet( )) { Set otherSet = map2.get(entry.getKey( )); if (otherSet == null) map2.put(entry.getKey( ), entry.getValue ( )); else otherSet.addAll(entry.getValue( )); } 

我不知道你的参数Set是什么,因此 :替换为适当的。

像这样(未经测试):

 // Assume all maps are of the same generic type. public static Map> mergeAll(Map m1, Map m2) { Map> merged = new HashMap(); // Merge commom entries into the new map. for (Map.Entry> entry : m1.entrySet()) { String key = entry.getKey(); Set s1 = new HashSet(entry.getValue()); Set s2 = m2.get(key); if (s2 != null) s1.addAll(s2); merged.put(key, s1); } // Add entries unique to m2 to the new map. for (String key : m2.keys()) { if (!s1.containsKey(key)) merged.put(key, new HashSet(m2.get(key))); } return merged; } 

请注意,此解决方案不会改变其任何参数。

 Map m1=new HashMap(); Map m2=new HashMap(); m1.put(1,"one"); m1.put(2,"two"); m2.put(3,"three"); m2.put(2,"two"); Set s=m2.keySet(); for(int i:s){ if(m1.get(i)==null){ m1.put(i,m2.get(i)); } } System.out.println(m1); 

请注意,如果您不希望仅使用第三个映射作为输出并为每个键创建一个新集合,则所有其他答案最终将扩充您可能不希望所有用例的原始集合。

 public static void merge2Maps(Map> a, Map> b, Map> c){ for (Map.Entry> entry : a.entrySet()) { Set set = new HashSet(); c.put(entry.getKey(), set); set.addAll(entry.getValue()); } for (Map.Entry> entry : b.entrySet()) { String key = entry.getKey(); Set set = c.get(key); if (set == null) { set = new HashSet(); c.put(entry.getKey(), set); } set.addAll(entry.getValue()); } } 

如果您希望最终使用不可变数据结构来防止操作合并的映射和映射的Set实例,那么您可以采用这种方法。 此解决方案使用Google的Guava库。

 public  Map> mergeToImmutable ( final Map> left, final Map> right) { return Maps.toMap( Sets.union( checkNotNull(left).keySet(), checkNotNull(right).keySet() ), new Function> () { @Override public Set apply (K input) { return ImmutableSet.builder() .addAll(MoreObjects.firstNonNull(left.get(input), Collections.emptySet())) .addAll(MoreObjects.firstNonNull(right.get(input), Collections.emptySet())) .build(); } } ); } 

如果您定义一个方法来将非null Set s统一为:

 static  Set union(Set... sets) { return Stream.of(sets) .filter(s -> s != null) .flatMap(Set::stream) .collect(Collectors.toSet()); } 

然后合并具有Set值的两个映射m1m2可以如下执行:

 Map merged = union(m1.keySet(), m2.keySet()) .stream() .collect(Collectors.toMap(k -> k, k -> union(m1.get(k), m2.get(k)))); 

甚至更简单:

 Map merged = new HashMap<>(); for (String k : union(m1.keySet(), m2.keySet()) merged.put(k, union(m1.get(k), m2.get(k))); 
  Map> mergeMapOfLists(Stream>> stream) { return stream .map(Map::entrySet) // convert each map to set of map's entries .flatMap(Collection::stream) // convert each map entry to stream and flat them to one stream .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (list1, list2) -> { list1.addAll(list2); return list1; })); // convert stream to map; if key is duplicated execute merge fuction (append exisitng list with elements from new list) }