合并两个地图
我有两个映射,其键是String
s,其值是Set
。 给定两个Map
,合并它们的最简单方法是什么,如果两个键相同,则值是两个集合的并集。 您可以假设值永远不为空,如果它有用,我们可以制作这些Map
的SortedMap
。
我们在谈论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()
都是Map
和Set
可选方法。 因此(为了获得O(1)查找),我建议使用HashMap
和HashSet
。
请注意,因为HashSet
或HashMap
都不是同步的,所以如果您需要线程安全的代码,则需要寻找其他解决方案。
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
值的两个映射m1
和m2
可以如下执行:
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