Как я могу объединить два объекта HashMap, содержащих одинаковые типы?

241

У меня есть два HashMapобъекта, определенные так:

HashMap<String, Integer> map1 = new HashMap<String, Integer>();
HashMap<String, Integer> map2 = new HashMap<String, Integer>();

У меня тоже третий HashMap объект:

HashMap<String, Integer> map3;

Как я могу слиться map1и map2объединиться в map3?

Mavin
источник
16
Вы не указали, что хотите сделать, если ключ существует на обеих картах.
Майкл Шепер

Ответы:

344
map3 = new HashMap<>();

map3.putAll(map1);
map3.putAll(map2);
a_horse_with_no_name
источник
1
спасибо, я объединяю Карты в цикл for, который использует метод возврата карты, и мне нужно объединить его с другой картой и применить тот же метод agian. Для этого я получаю исключение нулевого указателя с методом putAll. это не помогает использовать блок try / catch. что я должен делать? Я применяю, если условие, что, если размер == o, то не применять putAll еще применить его и так далее ...
Mavin
1
Если вы получаете NPE, то, очевидно, вы не правильно инициализировали один из ваших объектов. Вы печатаете трассировку стека в блоке catch? Итак, вы знаете, где возникает проблема. Но если вы не публикуете полный и точный код, включая трассировку стека, вам придется отслеживать его самостоятельно.
a_horse_with_no_name
95
Обратите внимание, что с этим решением, если ключ существует на обеих картах, значение в map2 будет сохранено, а значение в map1 потеряно.
Майкл Шепер
5
@MichaelScheper: что еще вы ожидаете? Ключи в Mapпо определению уникальны
a_horse_with_no_name
42
Я не знаю, чего ожидает Оператор. Возможно, он ожидает, что значения map1 будут иметь приоритет, или будет выброшено исключение, или для выполнения некоторой операции «слияния» с пересекающимися целыми числами. Или, может быть, так как это вопрос новичка, это тот случай, который ОПР не рассматривал, и в этом случае мой комментарий будет полезен.
Майкл Шепер
109

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

map3 = new HashMap<>(map1);
map3.putAll(map2);

Если вам нужно больше контроля над тем, как комбинируются значения, вы можете использовать Map.merge, добавленный в Java 8, который использует предоставленный пользователем BiFunctionдля объединения значений для дубликатов ключей. mergeработает с отдельными ключами и значениями, поэтому вам нужно использовать цикл или Map.forEach. Здесь мы объединяем строки для дубликатов ключей:

map3 = new HashMap<>(map1);
for (Map.Entry<String, String> e : map2.entrySet())
    map3.merge(e.getKey(), e.getValue(), String::concat);
//or instead of the above loop
map2.forEach((k, v) -> map3.merge(k, v, String::concat));

Если вы знаете, что у вас нет дубликатов ключей и хотите применить их, вы можете использовать функцию слияния, которая выдает AssertionError:

map2.forEach((k, v) ->
    map3.merge(k, v, (v1, v2) ->
        {throw new AssertionError("duplicate values for key: "+k);}));

Отступив от этого конкретного вопроса, библиотека потоков Java 8 предоставляет toMapи groupingBy Collectors . Если вы многократно объединяете карты в цикле, вы можете реструктурировать свои вычисления, чтобы использовать потоки, которые могут как прояснить ваш код, так и обеспечить легкий параллелизм, используя параллельный поток и параллельный коллектор.

Джеффри Босбом
источник
46

Однострочное использование Java 8 Stream API:

map3 = Stream.of(map1, map2).flatMap(m -> m.entrySet().stream())
       .collect(Collectors.toMap(Entry::getKey, Entry::getValue))

Среди преимуществ этого метода - возможность передавать функцию слияния, которая будет работать со значениями, имеющими один и тот же ключ, например:

map3 = Stream.of(map1, map2).flatMap(m -> m.entrySet().stream())
       .collect(Collectors.toMap(Entry::getKey, Entry::getValue, Math::max))
Виталий Федоренко
источник
1
это вызовет исключение IllegalStateException для дубликатов ключей
Arpit J.
1
@ArpitJ. в этом весь смысл второго варианта. Иногда вы хотите исключения, иногда нет.
Алекс Р
36

Java 8 альтернативный однострочный для объединения двух карт:

defaultMap.forEach((k, v) -> destMap.putIfAbsent(k, v));

То же самое со ссылкой на метод:

defaultMap.forEach(destMap::putIfAbsent);

Или идемпонент для решения оригинальной карты с третьей картой:

Map<String, Integer> map3 = new HashMap<String, Integer>(map2);
map1.forEach(map3::putIfAbsent);

И вот способ объединить две карты в быструю неизменяемую с Гуавой, которая выполняет наименьшие возможные промежуточные операции копирования:

ImmutableMap.Builder<String, Integer> builder = ImmutableMap.<String, Integer>builder();
builder.putAll(map1);
map2.forEach((k, v) -> {if (!map1.containsKey(k)) builder.put(k, v);});
ImmutableMap<String, Integer> map3 = builder.build();

См. Также Объединение двух карт с Java 8 для случаев, когда значения, присутствующие в обеих картах, должны быть объединены с функцией отображения.

Vadzim
источник
32

Если вам не нужна изменчивость для вашей окончательной карты, есть Guava ImmutableMap с его Builderи putAllметодом, который, в отличие от метода интерфейса JavaMap , может быть связан.

Пример использования:

Map<String, Integer> mergeMyTwoMaps(Map<String, Integer> map1, Map<String, Integer> map2) {
  return ImmutableMap.<String, Integer>builder()
      .putAll(map1)
      .putAll(map2)
      .build();
}

Конечно, этот метод может быть более общим, использовать varargs и цикл для putAll Maps аргументов и т. Д., Но я хотел показать концепцию.

Кроме того, ImmutableMapи у него Builderесть несколько ограничений (или, возможно, функции?):

  • они ноль враждебны NullPointerException - если какой-либо ключ или значение на карте равно нулю)
  • Builder не принимает дубликаты ключей (выдает, IllegalArgumentExceptionесли дубликаты ключей были добавлены).
Xaerxess
источник
21

Вы можете использовать Collection.addAll () для других типов, например List, Setи т. Д. Для Map, вы можете использовать putAll.

fastcodejava
источник
11

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

На месте:

public static <K, V> void mergeInPlace(Map<K, V> map1, Map<K, V> map2,
        BinaryOperator<V> combiner) {
    map2.forEach((k, v) -> map1.merge(k, v, combiner::apply));
}

Возвращаем новую карту:

public static <K, V> Map<K, V> merge(Map<K, V> map1, Map<K, V> map2,
        BinaryOperator<V> combiner) {
    Map<K, V> map3 = new HashMap<>(map1);
    map2.forEach((k, v) -> map3.merge(k, v, combiner::apply));
    return map3;
}
ZhekaKozlov
источник
2

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

static public <K, V> Map<K, V> merge(Map<K, V>... args) {
    final Map<K, V> buffer = new HashMap<>();

    for (Map m : args) {
        buffer.putAll(m);
    }

    return buffer;
}
Томас Деко
источник
2

Вы можете использовать HashMap<String, List<Integer>>для объединения обоих хэш-карт и избежать потери элементов в паре с одним и тем же ключом.

HashMap<String, Integer> map1 = new HashMap<>();
HashMap<String, Integer> map2 = new HashMap<>();
map1.put("key1", 1);
map1.put("key2", 2);
map1.put("key3", 3);
map2.put("key1", 4);
map2.put("key2", 5);
map2.put("key3", 6);
HashMap<String, List<Integer>> map3 = new HashMap<>();
map1.forEach((str, num) -> map3.put(str, new ArrayList<>(Arrays.asList(num))));
//checking for each key if its already in the map, and if so, you just add the integer to the list paired with this key
for (Map.Entry<String, Integer> entry : map2.entrySet()) {
    Integer value = entry.getValue();
    String key = entry.getKey();
    if (map3.containsKey(key)) {
        map3.get(key).add(value);
    } else {
        map3.put(key, new ArrayList<>(Arrays.asList(value)));
    }
}
map3.forEach((str, list) -> System.out.println("{" + str + ": " + list + "}"));

вывод:

{key1: [1, 4]}
{key2: [2, 5]}
{key3: [3, 6]}
Омер Вишлицкий
источник
2

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

Map<String, List<String>> map1 = new HashMap<>();
map1.put("India", Arrays.asList("Virat", "Mahi", "Rohit"));
map1.put("NZ", Arrays.asList("P1","P2","P3"));

Map<String, List<String>> map2 = new HashMap<>();
map2.put("India", Arrays.asList("Virat", "Mahi", "Rohit"));
map2.put("NZ", Arrays.asList("P1","P2","P4"));

Map<String, List<String>> collect4 = Stream.of(map1, map2)
                .flatMap(map -> map.entrySet().stream())
                .collect(
                        Collectors.toMap(
                                Map.Entry::getKey,
                                Map.Entry::getValue,
                                (strings, strings2) -> {
                                    List<String> newList = new ArrayList<>();
                                    newList.addAll(strings);
                                    newList.addAll(strings2);
                                    return newList;
                                }
                        )
                );
collect4.forEach((s, strings) -> System.out.println(s+"->"+strings));

Это дает следующий вывод

NZ->[P1, P2, P3, P1, P2, P4]
India->[Virat, Mahi, Rohit, Virat, Mahi, Rohit]
Ишан Бхатт
источник
0
    HashMap<Integer,String> hs1 = new HashMap<>();
    hs1.put(1,"ram");
    hs1.put(2,"sita");
    hs1.put(3,"laxman");
    hs1.put(4,"hanuman");
    hs1.put(5,"geeta");

    HashMap<Integer,String> hs2 = new HashMap<>();
    hs2.put(5,"rat");
    hs2.put(6,"lion");
    hs2.put(7,"tiger");
    hs2.put(8,"fish");
    hs2.put(9,"hen");

    HashMap<Integer,String> hs3 = new HashMap<>();//Map is which we add

    hs3.putAll(hs1);
    hs3.putAll(hs2);

    System.out.println(" hs1 : " + hs1);
    System.out.println(" hs2 : " + hs2);
    System.out.println(" hs3 : " + hs3);

Повторяющиеся элементы не будут добавлены (то есть дубликаты ключей), так как при печати hs3 мы получим только одно значение для ключа 5, которое будет последним добавленным значением, и это будет крыса. ** [Set имеет свойство не разрешать дубликат ключа, но значения могут быть дублированы]

Karunesh
источник
0

Способ 1: положить карты в список, а затем присоединиться

public class Test15 {
public static void main(String[] args) {

    Map<String, List<String>> map1 = new HashMap<>();
    map1.put("London", Arrays.asList("A", "B", "C"));
    map1.put("Wales", Arrays.asList("P1", "P2", "P3"));

    Map<String, List<String>> map2 = new HashMap<>();
    map2.put("Calcutta", Arrays.asList("Protijayi", "Gina", "Gini"));
    map2.put("London", Arrays.asList( "P4", "P5", "P6"));
    map2.put("Wales", Arrays.asList( "P111", "P5555", "P677666"));

    System.out.println(map1);System.out.println(map2);



    // put the maps in an ArrayList

    List<Map<String, List<String>>> maplist = new ArrayList<Map<String,List<String>>>();
    maplist.add(map1);
    maplist.add(map2);
    /*
<T,K,U> Collector<T,?,Map<K,U>> toMap(

                                  Function<? super T,? extends K> keyMapper,

                                  Function<? super T,? extends U> valueMapper,

                                  BinaryOperator<U> mergeFunction)
    */

 Map<String, List<String>> collect = maplist.stream()
    .flatMap(ch -> ch.entrySet().stream())
    .collect(
            Collectors.toMap(

            //keyMapper,

            Entry::getKey,

            //valueMapper
            Entry::getValue,

            // mergeFunction
     (list_a,list_b) -> Stream.concat(list_a.stream(), list_b.stream()).collect(Collectors.toList())

            ));



    System.out.println("Final Result(Map after join) => " + collect);
    /*
    {Wales=[P1, P2, P3], London=[A, B, C]}
{Calcutta=[Protijayi, Gina, Gini], Wales=[P111, P5555, P677666], London=[P4, P5, P6]}
Final Result(Map after join) => {Calcutta=[Protijayi, Gina, Gini], Wales=[P1, P2, P3, P111, P5555, P677666], London=[A, B, C, P4, P5, P6]}
*/

}//main


}

Способ 2: объединение карты нормалей

public class Test15 {
public static void main(String[] args) {

    Map<String, List<String>> map1 = new HashMap<>();
    map1.put("London", Arrays.asList("A", "B", "C"));
    map1.put("Wales", Arrays.asList("P1", "P2", "P3"));

    Map<String, List<String>> map2 = new HashMap<>();
    map2.put("Calcutta", Arrays.asList("Protijayi", "Gina", "Gini"));
    map2.put("London", Arrays.asList( "P4", "P5", "P6"));
    map2.put("Wales", Arrays.asList( "P111", "P5555", "P677666"));

    System.out.println(map1);System.out.println(map2);




    /*
<T,K,U> Collector<T,?,Map<K,U>> toMap(

                                  Function<? super T,? extends K> keyMapper,

                                  Function<? super T,? extends U> valueMapper,

                                  BinaryOperator<U> mergeFunction)
    */


Map<String, List<String>> collect = Stream.of(map1,map2)
    .flatMap(ch -> ch.entrySet().stream())
    .collect(
            Collectors.toMap(

            //keyMapper,

            Entry::getKey,

            //valueMapper
            Entry::getValue,

            // mergeFunction
     (list_a,list_b) -> Stream.concat(list_a.stream(), list_b.stream()).collect(Collectors.toList())

            ));



    System.out.println("Final Result(Map after join) => " + collect);
    /*
    {Wales=[P1, P2, P3], London=[A, B, C]}
{Calcutta=[Protijayi, Gina, Gini], Wales=[P111, P5555, P677666], London=[P4, P5, P6]}
Final Result(Map after join) => {Calcutta=[Protijayi, Gina, Gini], Wales=[P1, P2, P3, P111, P5555, P677666], London=[A, B, C, P4, P5, P6]}

*/

}//main


}
Судипта Датта
источник
0

Вы можете использовать функцию putAll для Map, как описано в коде ниже

HashMap<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("a", 1);
map1.put("b", 2);
map1.put("c", 3);
HashMap<String, Integer> map2 = new HashMap<String, Integer>();
map1.put("aa", 11);
map1.put("bb", 12);
HashMap<String, Integer> map3 = new HashMap<String, Integer>();
map3.putAll(map1);
map3.putAll(map2);
map3.keySet().stream().forEach(System.out::println);
map3.values().stream().forEach(System.out::println);
П Миттал
источник
0

Ниже сниппет занимает более одной карты и объединяет их.

 private static <K, V> Map<K, V> combineMaps(Map<K, V>... maps) {
        if (maps == null || maps.length == 0) {
            return Collections.EMPTY_MAP;
        }

        Map<K, V> result = new HashMap<>();

        for (Map<K, V> map : maps) {
            result.putAll(map);
        }
        return result;
    }

Демо пример ссылки.

Хари Кришна
источник
-1

Вы можете использовать - метод addAll

http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html

Но всегда есть проблема, что - если ваши две хэш-карты имеют одинаковый ключ - тогда он переопределит значение ключа из первой хеш-карты на значение ключа из второй хеш-карты.

Для большей безопасности - измените значения ключей - вы можете использовать префикс или суффикс на ключах - (другой префикс / суффикс для первой карты хэша и другой префикс / суффикс для второй карты хэша)

Ашиш Шеткар
источник