2012-01-09 62 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); 
    }); 
}