¿Cómo puedo combinar dos objetos HashMap que contienen los mismos tipos?

241

Tengo dos HashMapobjetos definidos así:

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

También tengo un tercer HashMapobjeto:

HashMap<String, Integer> map3;

¿Cómo puedo fusionarme map1y map2unirme map3?

Mavin
fuente
16
No ha indicado qué desea que suceda si existe una clave en ambos mapas.
Michael Scheper

Respuestas:

344
map3 = new HashMap<>();

map3.putAll(map1);
map3.putAll(map2);
un caballo sin nombre
fuente
1
gracias, estoy fusionando los mapas en un bucle for que usa un método para devolver un mapa y necesito fusionarlo con otro mapa y aplicar el mismo método agian. Para esto, obtengo una excepción de puntero nulo con el método putAll. no ayuda usar el bloque try / catch. ¿Qué tengo que hacer? Estoy aplicando si condición, que si size == o entonces no aplica putAll lo demás aplica y así sucesivamente ...
Mavin
1
Si obtiene un NPE, aparentemente no inicializó uno de sus objetos correctamente. ¿Imprime el stacktrace en el bloque catch? Entonces ya sabes dónde surge el problema. Pero a menos que publique el código completo y exacto, incluido el seguimiento de la pila, deberá rastrearlo por su cuenta.
a_horse_with_no_name
95
Tenga en cuenta que, con esta solución, si existe una clave en ambos mapas, el valor en map2 se conservará y el valor en map1 se perderá.
Michael Scheper
55
@MichaelScheper: ¿qué más esperas? Las claves en a Mapson por definición únicas
a_horse_with_no_name
42
No sé qué espera el Operador. Tal vez él espera que los valores de map1 tengan precedencia, o que se lance una excepción, o que se realice alguna operación de 'fusión' en Integer que se cruzan. O tal vez, dado que esta es una pregunta para principiantes, este es un caso que el OPer no había considerado, en cuyo caso mi comentario sería útil.
Michael Scheper
109

Si sabe que no tiene claves duplicadas, o si desea que los valores map2sobrescriban los valores de map1claves duplicadas, simplemente puede escribir

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

Si necesita más control sobre cómo se combinan los valores, puede usarlos Map.merge, agregados en Java 8, que usa un valor proporcionado por el usuario BiFunctionpara fusionar valores para claves duplicadas. mergeopera en claves y valores individuales, por lo que deberá usar un bucle o Map.forEach. Aquí concatenamos cadenas para claves 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));

Si sabe que no tiene claves duplicadas y desea aplicarlas, puede usar una función de combinación que arroje un AssertionError:

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

Dando un paso atrás de esta pregunta específica, la biblioteca de secuencias de Java 8 proporciona toMapy groupingBy recopiladores . Si fusiona mapas repetidamente en un bucle, es posible que pueda reestructurar su cálculo para usar flujos, lo que puede aclarar su código y permitir un paralelismo fácil usando un flujo paralelo y un colector concurrente.

Jeffrey Bosboom
fuente
46

One-liner utilizando Java 8 Stream API:

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

Entre los beneficios de este método está la capacidad de pasar una función de fusión, que tratará con valores que tienen la misma clave, por ejemplo:

map3 = Stream.of(map1, map2).flatMap(m -> m.entrySet().stream())
       .collect(Collectors.toMap(Entry::getKey, Entry::getValue, Math::max))
Vitalii Fedorenko
fuente
1
esto arrojará IllegalStateException para claves duplicadas
Arpit J.
1
@ArpitJ. Ese es el objetivo de la segunda variación. A veces quieres la excepción, a veces no.
Alex R
36

Java 8 alternativo de una sola línea para fusionar dos mapas:

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

Lo mismo con la referencia del método:

defaultMap.forEach(destMap::putIfAbsent);

O idemponente para la solución de mapas original con el tercer mapa:

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

Y aquí hay una manera de fusionar dos mapas en uno rápido e inmutable con Guava que realiza las operaciones de copia intermedia menos posibles:

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 también Combinar dos mapas con Java 8 para ver los casos en que los valores presentes en ambos mapas deben combinarse con la función de mapeo.

Vadzim
fuente
32

Si no necesita mutabilidad para su mapa final, está Guava's ImmutableMap con su métodoBuilder y que, en contraste con el método de interfaz de Java , puede encadenarse.putAllMap

Ejemplo de uso:

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

Por supuesto, este método puede ser más genérico, usar varargs y bucles a putAll Mapspartir de argumentos, etc., pero quería mostrar un concepto.

Además, ImmutableMapy Buildertiene pocas limitaciones (¿o quizás características?):

  • son nulos hostiles (lanzamiento NullPointerException- si alguna clave o valor en el mapa es nulo)
  • El constructor no acepta claves duplicadas (arroja IllegalArgumentExceptionsi se agregaron claves duplicadas).
Xaerxess
fuente
21

Se podría utilizar Collection.addAll () para otros tipos, por ejemplo List, Set, etc. Para Map, se puede utilizar putAll.

fastcodejava
fuente
11

Solución genérica para combinar dos mapas que posiblemente puedan compartir claves comunes:

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

Devolviendo un nuevo 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
fuente
2

Un pequeño fragmento que uso muy a menudo para crear mapas a partir de otros 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
fuente
2

puede usar HashMap<String, List<Integer>>para combinar ambos hashmaps y evitar perder elementos emparejados con la misma clave.

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

salida:

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

Muy tarde, pero déjenme compartir lo que hice cuando tuve el mismo 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));

Da el siguiente resultado

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

No se agregarán elementos duplicados (es decir, claves duplicadas) ya que cuando imprimiremos hs3 obtendremos solo un valor para la clave 5, que será el último valor agregado y será rat. ** [Set tiene la propiedad de no permitir la clave duplicada pero los valores pueden duplicarse]

Karunesh
fuente
0

Método 1: poner mapas en una lista y luego unirse

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: fusión 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
fuente
0

Puede usar la función putAll para Map como se explica en el siguiente código

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
fuente
0

El siguiente fragmento toma más de un mapa y los combina.

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

Enlace de ejemplo de demostración .

Hari Krishna
fuente
-1

puedes usar - método addAll

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

Pero siempre existe el problema de que, si sus dos mapas hash tienen alguna clave igual, entonces anulará el valor de la clave del primer mapa hash con el valor de la clave del segundo mapa hash.

Para estar en el lado más seguro, cambie los valores de las teclas, puede usar prefijo o sufijo en las teclas (diferentes prefijos / sufijos para el primer mapa hash y diferentes prefijos / sufijos para el segundo mapa hash)

Ashish Shetkar
fuente