キーがString
sで値がSet<MyObject>
。 2つのMap
sが与えられた場合、2つのキーが同一である場合、値は2つのセットの和集合になるように、それらをマージする最も簡単な方法は何ですか。値は決してnullではないと仮定でき、それが有用であれば、これらのMap
s SortedMap
sを作成できます。
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() );
}
}
これは 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
sに分割し、両方の値を新しい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
のタイプを決定できます。
これはどうですか(未テスト):
_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
。
HashSet
もHashMap
も同期されていないため、スレッドセーフコードが必要な場合は、他のソリューションを探す必要があることに注意してください。
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);
});
}
以下は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( ));
}
Set
sをパラメーター化したものがわからないため、<???>
:必要に応じて置き換えます。
このようなもの(未テスト):
// 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;
}
このソリューションでは、引数のいずれも変更されないことに注意してください。
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);
<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)
}
Null以外のSet
sを結合するメソッドを次のように定義する場合:
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)));
マージされたマップとマップの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();
}
}
);
}
出力として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());
}
}