web-dev-qa-db-ja.com

2つのマップを結合する

キーがStringsで値がSet<MyObject>。 2つのMapsが与えられた場合、2つのキーが同一である場合、値は2つのセットの和集合になるように、それらをマージする最も簡単な方法は何ですか。値は決してnullではないと仮定でき、それが有用であれば、これらのMaps SortedMapsを作成できます。

24
Dave

HashMapインスタンスについて話していますか。その場合、ルックアップはO(1)なので、1つのマップを取得し、そのマップのエントリを反復処理し、他のマップにそのキーが含まれているかどうかを確認できます。そうでない場合は、セットを追加します。キーが含まれている場合は、2つのセットの結合を取ります(1つのセットの すべての要素を追加 によって)

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

これは 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;
        }));

これは、マップをEntrysに分割し、両方の値を新しい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の3番目のパラメーターは、重複キーがない場合は不要です。

別の Collectors.toMap があり、4番目のパラメーターを使用して、収集されるMapのタイプを決定できます。

28
Alex

これはどうですか(未テスト):

_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() は、MapおよびSetのオプションのメソッドであることに注意してください。したがって(およびO(1)ルックアップを取得するため)、 HashMap および HashSet

HashSetHashMapも同期されていないため、スレッドセーフコードが必要な場合は、他のソリューションを探す必要があることに注意してください。

4
Timothy Jones
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
Suban Asif

以下はmap1 into 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( ));
}

Setsをパラメーター化したものがわからないため、<???>:必要に応じて置き換えます。

1
Mac

このようなもの(未テスト):

// 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
maerics
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
<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)
}
0
Karol Król

Null以外のSetsを結合するメソッドを次のように定義する場合:

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

次に、2つのマップをマージしますm1およびm2を持つSet<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)));
0
Alex Salauyou

マージされたマップとマップの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
justin.hughey

出力として3番目のマップを使用し、キーごとに新しいセットを作成したくない場合は、他のすべての答えが最終的にすべてのユースケースに必要ない可能性がある元のセットを補強することに注意してください

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());
    }
}
0
Aurélien Ooms