Wie kann ich zwei HashMap-Objekte mit denselben Typen kombinieren?

240

Ich habe zwei HashMapObjekte wie folgt definiert:

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

Ich habe auch ein drittes HashMapObjekt:

HashMap<String, Integer> map3;

Wie kann ich fusionieren map1und map2gemeinsam in map3?

Mavin
quelle
16
Sie haben nicht angegeben, was passieren soll, wenn in beiden Karten ein Schlüssel vorhanden ist.
Michael Scheper

Antworten:

343
map3 = new HashMap<>();

map3.putAll(map1);
map3.putAll(map2);
ein Pferd ohne Name
quelle
1
Vielen Dank, ich füge die Maps in einer for-Schleife zusammen, die eine Methode verwendet, um eine Map zurückzugeben, und muss sie mit einer anderen Map zusammenführen und dieselbe Methode erneut anwenden. Dafür bekomme ich eine Nullzeigerausnahme mit der putAll-Methode. Es hilft nicht, den Try / Catch-Block zu verwenden. was soll ich machen? Ich bewerbe mich, wenn Bedingung, dass wenn Größe == o, dann nicht anwenden putAll sonst anwenden und so weiter ....
Mavin
1
Wenn Sie eine NPE erhalten, haben Sie anscheinend eines Ihrer Objekte nicht richtig initialisiert. Drucken Sie die Stapelspur im Fangblock? Sie wissen also, wo das Problem auftritt. Aber wenn Sie nicht den vollständigen und genauen Code einschließlich des Stack-Trace veröffentlichen, müssen Sie diesen selbst aufspüren.
a_horse_with_no_name
94
Beachten Sie, dass bei dieser Lösung, wenn in beiden Karten ein Schlüssel vorhanden ist, der Wert in Karte2 erhalten bleibt und der Wert in Karte1 verloren geht.
Michael Scheper
5
@ MichaelScheper: Was erwartest du noch? Schlüssel in a Mapsind per Definition eindeutig
a_horse_with_no_name
41
Ich weiß nicht, was der OPer erwartet. Vielleicht erwartet er, dass die map1-Werte Vorrang haben oder eine Ausnahme ausgelöst wird oder dass eine 'Zusammenführungs'-Operation für sich überschneidende Ganzzahlen ausgeführt wird. Oder vielleicht, da dies eine Anfängerfrage ist, ist dies ein Fall, den der OPer nicht berücksichtigt hat. In diesem Fall wäre mein Kommentar hoffentlich hilfreich.
Michael Scheper
109

Wenn Sie wissen, dass Sie keine doppelten Schlüssel haben oder dass Werte für doppelte Schlüssel map2überschrieben werden sollen map1, können Sie einfach schreiben

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

Wenn Sie mehr Kontrolle darüber benötigen, wie Werte kombiniert werden, können Sie das Map.mergein Java 8 hinzugefügte verwenden, das einen vom Benutzer bereitgestellten BiFunctionWert zum Zusammenführen von Werten für doppelte Schlüssel verwendet. mergearbeitet mit einzelnen Schlüsseln und Werten, daher müssen Sie eine Schleife oder verwenden Map.forEach. Hier verketten wir Zeichenfolgen für doppelte Schlüssel:

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

Wenn Sie wissen, dass Sie keine doppelten Schlüssel haben und diese erzwingen möchten, können Sie eine Zusammenführungsfunktion verwenden, die Folgendes auslöst AssertionError:

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

Die Java 8-Streams-Bibliothek toMapund groupingBy Collectors bieten einen Schritt zurück von dieser speziellen Frage . Wenn Sie Karten wiederholt in einer Schleife zusammenführen, können Sie Ihre Berechnung möglicherweise so umstrukturieren, dass Streams verwendet werden. Dies kann sowohl Ihren Code verdeutlichen als auch eine einfache Parallelität mithilfe eines parallelen Streams und eines gleichzeitigen Kollektors ermöglichen.

Jeffrey Bosboom
quelle
46

Einzeiler mit Java 8 Stream API:

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

Zu den Vorteilen dieser Methode gehört die Möglichkeit, eine Zusammenführungsfunktion zu übergeben, die sich mit Werten befasst, die denselben Schlüssel haben, zum Beispiel:

map3 = Stream.of(map1, map2).flatMap(m -> m.entrySet().stream())
       .collect(Collectors.toMap(Entry::getKey, Entry::getValue, Math::max))
Vitalii Fedorenko
quelle
1
Dies wird IllegalStateException für doppelte Schlüssel
auslösen
1
@ArpitJ. Das ist der springende Punkt der zweiten Variante. Manchmal möchten Sie die Ausnahme, manchmal nicht.
Alex R
36

Java 8 alternativer Einzeiler zum Zusammenführen von zwei Karten:

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

Das gleiche gilt für die Methodenreferenz:

defaultMap.forEach(destMap::putIfAbsent);

Oder idemponent für ursprüngliche Kartenlösung mit dritter Karte:

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

Und hier ist eine Möglichkeit, zwei Karten mit Guava zu einer schnell unveränderlichen zusammenzuführen , die am wenigsten mögliche Zwischenkopiervorgänge ausführt:

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

Siehe auch Zusammenführen von zwei Karten mit Java 8 für Fälle, in denen Werte, die in beiden Karten vorhanden sind, mit der Zuordnungsfunktion kombiniert werden müssen.

Vadzim
quelle
32

Wenn Sie für Ihre endgültige Karte keine Veränderlichkeit benötigen, gibt es Guavas ImmutableMap mit seiner Builderund putAll-Methode, die im Gegensatz zur Java- MapSchnittstellenmethode verkettet werden kann.

Anwendungsbeispiel:

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

Natürlich kann diese Methode allgemeiner sein, Varargs und Loop verwenden, um putAll MapsArgumente usw. zu verwenden, aber ich wollte ein Konzept zeigen.

Außerdem ImmutableMapund es Buildergibt nur wenige Einschränkungen (oder vielleicht Funktionen?):

  • Sie sind null feindlich (werfen NullPointerException- wenn ein Schlüssel oder Wert in der Karte null ist)
  • Der Builder akzeptiert keine doppelten Schlüssel (wirft, IllegalArgumentExceptionwenn doppelte Schlüssel hinzugefügt wurden).
Xaerxess
quelle
21

Sie könnten verwenden Collection.addAll () für andere Arten, zum Beispiel List, Setusw. Map, können Sie verwenden putAll.

fastcodejava
quelle
11

Generische Lösung zum Kombinieren von zwei Karten, die möglicherweise gemeinsame Schlüssel haben können:

An Ort und Stelle:

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

Neue Karte zurückgeben:

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

Ein kleines Snippet, das ich sehr oft verwende, um Karten aus anderen Karten zu erstellen:

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

Sie können HashMap<String, List<Integer>>beide Hashmaps zusammenführen und vermeiden, dass Elemente verloren gehen, die mit demselben Schlüssel gepaart sind.

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

Ausgabe:

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

Sehr spät, aber lassen Sie mich mitteilen, was ich getan habe, als ich das gleiche Problem hatte.

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

Es gibt die folgende Ausgabe

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

Doppelte Elemente werden nicht hinzugefügt (dh doppelte Schlüssel), da beim Drucken von hs3 nur ein Wert für Schlüssel 5 angezeigt wird, der die letzte Wertschöpfung darstellt und eine Ratte ist. ** [Set hat die Eigenschaft, den doppelten Schlüssel nicht zuzulassen, aber Werte können doppelt vorhanden sein]

Karunesh
quelle
0

Methode 1: Karten in eine Liste einfügen und dann beitreten

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


}

Methode 2: Normale Kartenzusammenführung

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

Sie können die Funktion putAll für Map verwenden, wie im folgenden Code erläutert

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

Das folgende Snippet nimmt mehr als eine Karte und kombiniert sie.

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

Demo Beispiel Link.

Hari Krishna
quelle
-1

Sie können die Methode - addAll verwenden

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

Es gibt jedoch immer das Problem, dass - wenn Ihre beiden Hash-Maps einen gleichen Schlüssel haben - der Wert des Schlüssels aus der ersten Hash-Map mit dem Wert des Schlüssels aus der zweiten Hash-Map überschrieben wird.

Um auf der sicheren Seite zu sein - ändern Sie die Schlüsselwerte - Sie können Präfix oder Suffix für die Schlüssel verwenden - (anderes Präfix / Suffix für die erste Hash-Map und anderes Präfix / Suffix für die zweite Hash-Map)

Ashish Shetkar
quelle