Como posso combinar dois objetos HashMap que contêm os mesmos tipos?

241

Eu tenho dois HashMapobjetos definidos assim:

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

Eu também tenho um terceiro HashMapobjeto:

HashMap<String, Integer> map3;

Como posso mesclar map1e map2juntar map3?

Mavin
fonte
16
Você não declarou o que deseja que aconteça se houver uma chave nos dois mapas.
Michael Scheper

Respostas:

344
map3 = new HashMap<>();

map3.putAll(map1);
map3.putAll(map2);
um cavalo sem nome
fonte
1
obrigado, estou mesclando o Maps em um loop for que usa um método para retornar um mapa e preciso mesclá-lo com outro mapa e aplicar o mesmo método novamente. Para isso, recebo uma exceção de ponteiro nulo com o método putAll. isso não ajuda a usar o bloco try / catch. O que devo fazer? Estou aplicando se condição, que se tamanho == o então não se aplica putAll else aplicá-lo e assim por diante .... #
456 Mavin
1
Se você receber um NPE, aparentemente você não inicializou um de seus objetos corretamente. Você imprime o rastreamento de pilha no bloco de captura? Então você sabe onde o problema surge. Mas, a menos que você publique o código completo e exato, incluindo o rastreamento de pilha, será necessário rastrear isso sozinho.
a_horse_with_no_name
95
Observe que, com esta solução, se existir uma chave nos dois mapas, o valor no mapa2 será preservado e o valor no mapa1 será perdido.
Michael Scheper
5
@ MichaelScheper: o que mais você espera? As chaves em a Mapsão, por definição, exclusivas
a_horse_with_no_name 16/12
42
Não sei o que o operador espera. Talvez ele espere que os valores do mapa1 tenham precedência, ou que uma exceção seja lançada, ou que alguma operação de 'mesclagem' seja executada na interseção de números inteiros. Ou talvez, já que essa é uma pergunta para iniciantes, esse é um caso que o OPer não havia considerado, caso em que meu comentário seria útil.
Michael Scheper
109

Se você sabe que não possui chaves duplicadas ou deseja que os valores map2substituam os valores das map1chaves duplicadas, basta escrever

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

Se você precisar de mais controle sobre como os valores são combinados, poderá usar Map.merge, incluído no Java 8, que usa um fornecido pelo usuário BiFunctionpara mesclar valores para chaves duplicadas. mergeopera com chaves e valores individuais; portanto, você precisará usar um loop ou Map.forEach. Aqui concatenamos cadeias de caracteres para chaves duplicadas:

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

Se você sabe que não possui chaves duplicadas e deseja aplicá-las, você pode usar uma função de mesclagem que gera um AssertionError:

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

Retrocedendo nesta questão específica, a biblioteca de fluxos Java 8 fornece toMape groupingBy Collectors . Se você mesclar mapas repetidamente em um loop, poderá reestruturar sua computação para usar fluxos, o que pode esclarecer seu código e facilitar o paralelismo fácil usando um fluxo paralelo e um coletor simultâneo.

Jeffrey Bosboom
fonte
46

Linha única usando a API Java 8 Stream:

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

Entre os benefícios desse método está a capacidade de passar uma função de mesclagem, que lida com valores que possuem a mesma chave, por exemplo:

map3 = Stream.of(map1, map2).flatMap(m -> m.entrySet().stream())
       .collect(Collectors.toMap(Entry::getKey, Entry::getValue, Math::max))
Vitalii Fedorenko
fonte
1
este vai jogar IllegalStateException para chaves duplicadas
Arpit J.
1
@ArpitJ. esse é o objetivo da segunda variação. Às vezes você quer a exceção, às vezes não.
Alex R
36

Uma linha alternativa Java 8 para mesclar dois mapas:

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

O mesmo com a referência do método:

defaultMap.forEach(destMap::putIfAbsent);

Ou idemponente para a solução de mapas originais com o terceiro mapa:

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

E aqui está uma maneira de mesclar dois mapas em um rápido e imutável com o Guava, que realiza menos operações de cópia intermediária possíveis:

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

Consulte também Mesclar dois mapas com Java 8 para casos em que os valores presentes nos dois mapas precisam ser combinados com a função de mapeamento.

Vadzim
fonte
32

Se você não precisa de mutabilidade para o seu mapa final, existem Guava's ImmutableMap com its Buildere putAllmethod que, ao contrário do Mapmétodo de interface do Java , podem ser encadeados.

Exemplo de uso:

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

Obviamente, esse método pode ser mais genérico, usar varargs e loop para putAll Mapsargumentos etc., mas eu queria mostrar um conceito.

Além disso, ImmutableMape Buildertem poucas limitações (ou talvez recursos?):

  • eles são nulos hostis (throw NullPointerException- se qualquer chave ou valor no mapa for nulo)
  • O construtor não aceita chaves duplicadas (lança IllegalArgumentExceptionse chaves duplicadas foram adicionadas).
Xaerxess
fonte
21

Você poderia usar Collection.addAll () para outros tipos, por exemplo List, Set, etc. Por Map, você pode usar putAll.

fastcodejava
fonte
11

Solução genérica para combinar dois mapas que podem compartilhar chaves comuns:

No lugar:

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

Retornando um novo mapa:

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
fonte
2

Um pequeno trecho usado com frequência para criar mapa a partir de outros mapas:

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;
}
Thomas Decaux
fonte
2

você pode usar HashMap<String, List<Integer>>para mesclar os dois hashmaps e evitar a perda de elementos emparelhados com a mesma chave.

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 + "}"));

resultado:

{key1: [1, 4]}
{key2: [2, 5]}
{key3: [3, 6]}
Omer Vishlitzky
fonte
2

Muito tarde, mas deixe-me compartilhar o que fiz quando tive o mesmo problema.

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

Dá a seguinte saída

NZ->[P1, P2, P3, P1, P2, P4]
India->[Virat, Mahi, Rohit, Virat, Mahi, Rohit]
Ishan Bhatt
fonte
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);

Itens duplicados não serão adicionados (ou seja, chaves duplicadas); quando imprimirmos hs3, obteremos apenas um valor para a chave 5, que será o último valor adicionado e será rat. ** [O conjunto possui uma propriedade de não permitir a chave duplicada, mas os valores podem ser duplicados]

Karunesh
fonte
0

Método 1: Coloque mapas em uma lista e depois ingresse

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


}

Método 2: mesclagem de mapa normal

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


}
Soudipta Dutta
fonte
0

Você pode usar a função putAll para o mapa, conforme explicado no código abaixo

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);
P Mittal
fonte
0

O snippet abaixo pega mais de um mapa e combina-os.

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

Link de exemplo de demonstração .

Hari Krishna
fonte
-1

você pode usar - método addAll

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

Mas sempre há esse problema que - se seus dois mapas de hash tiverem uma chave igual -, ele substituirá o valor da chave do primeiro mapa de hash pelo valor da chave do segundo mapa de hash.

Por estar do lado mais seguro - altere os valores das chaves - você pode usar prefixo ou sufixo nas chaves - (prefixo / sufixo diferente para o primeiro mapa de hash e prefixo / sufixo diferente para o segundo mapa de hash)

Ashish Shetkar
fonte