2012-01-09 77 views
22

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

+3

如果你必须使用番石榴[Multimap之(https://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap),你可以简单地避免这个问题的方法可行,合并和putAll(Multimap其他)一样简单。 – Dag 2015-05-29 15:22:56

+0

类似,可能重复:http://stackoverflow.com/questions/4299728/how-can-i-combine-two-hashmap-objects-containing-the-same-types – 2015-08-20 18:20:39

+0

应该很容易做到[地图合并](https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#merge-KV-java.util.function.BiFunction-)方法。 – Roland 2016-08-29 16:29:56

回答

12

我们在谈论HashMap实例。在这种情况下,查找是O(1),因此您可以只取一张地图,遍历该地图的条目,查看其他地图是否包含该关键字。如果不是,只需添加该组。如果它包含的钥匙,拿着两套联盟(由一组到另一个的adding all elements

要使用一些代码,我在那里用了一套具有自动完成我的IDE说明

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()); 
    } 
} 
+3

这将跳过secondMap中存在的条目,但不会在firstMap – sam 2013-05-13 06:21:03

+0

sam中 - 他正在编辑secondMap,因此将更改secondMap。 – JFK 2015-02-23 15:15:36

+0

你可以使用--addAll方法 http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html 但总是有这个问题 - 如果你的两个散列图有任何关键字都相同 - 那么它将使用第二个哈希映射中的键的值覆盖第一个哈希映射中的键的值。 对于更安全的一面 - 更改键值 - 可以在键上使用前缀或后缀 - (第一个哈希映射使用不同的前缀/后缀,第二个哈希映射使用不同的前缀/后缀) – 2016-03-16 06:25:15

1

以下应合并一个map1map2(未经测试):

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 S:适当更换。

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都未同步,所以如果您需要线程安全代码,则需要寻找其他解决方案。

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; 
} 

请注意,此解决方案不发生变异或者它的参数。

+1

那些在' m2'但不在'm1'中? – 2012-01-09 22:33:54

+0

您可以调用'm2.getValue()',但'm2'是一个'Map',因此没有'getValue()'方法。 – 2012-01-09 22:40:59

+0

@MichaelMcGowan:哦,对,修正了这个问题(geez,看看会发生什么事情,当我尝试编码掉头顶!) – maerics 2012-01-09 22:43:28

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); 
+0

这是一个简单的程序,解释如何合并两个地图 – user3301756 2014-02-12 13:10:09

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()); 
    } 
} 
21

您可以用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秒,然后用01加入他们其中resolves 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的类型。

+4

更简洁一点就是使用' Stream.concat(first.entrySet()。stream(),second.entrySet()。stream())'并且避免'map'和'flatMap'。 – 2015-03-03 11:00:17

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(); 
      } 
     } 
    ); 
} 
0

如果定义团结非空Set个方法为:

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

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

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))); 
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) 
} 
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); 
    }); 
}