Подтвердить что ты не робот

Слияние двух карт

У меня есть две карты, ключи которых String и значения которых Set<MyObject>. Учитывая два Map s, что является самым простым способом их слияния, так что, если два ключа идентичны, значение представляет собой объединение двух наборов. Вы можете предположить, что значения никогда не являются нулевыми, и если это полезно, мы можем сделать эти Map SortedMap s.

4b9b3361

Ответ 1

Мы говорим о экземплярах HashMap. В этом случае поиск равен O (1), поэтому вы можете просто взять одну карту, перебрать элементы этой карты, посмотреть, содержит ли другая карта этот ключ. Если нет, просто добавьте набор. Если он содержит ключ, возьмите объединение двух наборов (добавив все элементы одного набора в другой)

Чтобы проиллюстрировать некоторый код, где я использовал Set, чтобы иметь автозаполнение в моей среде 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() );
  }
}

Ответ 2

Вы можете сделать это с помощью 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, а затем соединяет их с 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 не нужен, если нет дубликатов ключей.

Существует еще один Collectors.toMap с четвертым параметром, который позволяет вам выбрать тип Map, собранный в.

Ответ 3

Как насчет этого (непроверенный):

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 не синхронизированы, вам нужно будет найти какое-то другое решение, если вы хотите использовать безопасный для потока код.

Ответ 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);
    });
}

Ответ 5

Следующее должно объединить a map1 в map2 (untested):

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, поэтому <???>: замените соответствующим образом.

Ответ 6

Что-то вроде этого (untested):

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

Обратите внимание, что это решение не мутирует ни один из его аргументов.

Ответ 7

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

Ответ 8

Обратите внимание, что все другие ответы в конечном итоге увеличат исходные наборы, которые могут не потребоваться для всех случаев использования, если вы не хотите, чтобы просто использовать третью карту в качестве вывода и создать новый набор для каждой клавиши

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

Ответ 9

Если вы хотите получить неизменяемые структуры данных, чтобы предотвратить манипулирование вашей объединенной картой и отображать множество экземпляров, вы можете воспользоваться этим подходом. Это решение использует библиотеку 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();
            }
        }
    );
}

Ответ 10

Если вы определяете метод объединения ненулевого Set как:

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

тогда объединение двух карт 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)));

Ответ 11

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