Как я могу объединить два объекта HashMap, содержащих одинаковые типы? - PullRequest
216 голосов
/ 29 ноября 2010

У меня есть два 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?

Ответы [ 12 ]

309 голосов
/ 29 ноября 2010
map3 = new HashMap<>();

map3.putAll(map1);
map3.putAll(map2);
95 голосов
/ 21 сентября 2014

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

42 голосов
/ 16 февраля 2016

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

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

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

map3 = Stream.of(map1, map2).flatMap(m -> m.entrySet().stream())
       .collect(Collectors.toMap(Entry::getKey, Entry::getValue, Math::max))
32 голосов
/ 31 июля 2015

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

27 голосов
/ 26 октября 2011

Если вам не нужна изменчивость для вашей окончательной карты, есть Гуава ImmutableMap с его Builder и putAll method , который, в отличие от Java Map интерфейсный метод , может быть соединен в цепочку.

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

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

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

Кроме того, ImmutableMap и его Builder имеют несколько ограничений (илиможет быть особенности?):

  • они являются недействительными (бросить NullPointerException - если какой-либо ключ или значение на карте имеют значение null)
  • Строитель не принимает дубликаты ключей (бросает IllegalArgumentException, если были добавлены дубликаты ключей).
25 голосов
/ 29 ноября 2010
17 голосов
/ 29 ноября 2010

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

11 голосов
/ 01 февраля 2017

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

На месте:

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;
}
1 голос
/ 30 сентября 2018

вы можете использовать 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]}
1 голос
/ 28 сентября 2018

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

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...