合并两个地图

时间:2012-01-09 22:19:04

标签: java map merge

我有两张地图,其密钥为String,其值为Set<MyObject>。给定两个Map s,合并它们的最简单方法是什么,如果两个键相同,则值是两个集合的并集。您可以假设值永远不会为空,如果它有用,我们可以生成这些Map s SortedMap

11 个答案:

答案 0 :(得分:26)

您可以轻松地使用stream执行此操作:

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

这会将地图拆分为Entry个,然后将Collectorresolves duplicates联系起来,方法是将这两个值添加到新的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的类型。

答案 1 :(得分:13)

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

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

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

答案 2 :(得分:4)

这个(未经测试)如何:

Map<String,Set<Whatever>> m1 = // input map
Map<String,Set<Whatever>> m2 =  // input map

Map<String,Set<Whatever>> 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都未同步,因此如果您需要线程安全的代码,则需要寻找其他解决方案。

答案 3 :(得分:4)

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

答案 4 :(得分:1)

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

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

我不知道您的Set参与了什么,因此<???>:视情况而定。

答案 5 :(得分:1)

像这样(未经测试):

// Assume all maps are of the same generic type.
public static Map<String, Set<MyObject>> mergeAll(Map m1, Map m2) {
  Map<String, Set<MyObject>> merged = new HashMap();
  // Merge commom entries into the new map.
  for (Map.Entry<String, Set<MyObject>> entry : m1.entrySet()) {
    String key = entry.getKey();
    Set<MyObject> s1 = new HashSet(entry.getValue());
    Set<MyObject> 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;
}

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

请注意,如果您不希望仅使用第三个地图作为输出并为每个键创建一个新集,那么所有其他答案最终会增加您可能不希望所有用例的原始集合

public static void merge2Maps(Map<String, Set<Double>> a, Map<String, Set<Double>> b, Map<String, Set<Double>> c){

    for (Map.Entry<String, Set<Double>> entry : a.entrySet()) {
        Set<Double> set = new HashSet<Double>();
        c.put(entry.getKey(), set);
        set.addAll(entry.getValue());
    }

    for (Map.Entry<String, Set<Double>> entry : b.entrySet()) {
        String key = entry.getKey();
        Set<Double> set = c.get(key);

        if (set == null) {
            set = new HashSet<Double>();
            c.put(entry.getKey(), set);
        }

        set.addAll(entry.getValue());
    }
}

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

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

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

然后合并两个具有m1值的地图m2Set<V>可以按如下方式执行:

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

甚至更简单:

Map<String, V> merged = new HashMap<>();
for (String k : union(m1.keySet(), m2.keySet())
     merged.put(k, union(m1.get(k), m2.get(k)));

答案 10 :(得分:0)

<K, V> Map<K, List<V>> mergeMapOfLists(Stream<Map<K, List<V>>> 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)
}