Zusammenführen von zwei Karten mit Java 8

Zusammenführen von zwei Karten mit Java 8

 

1. Einführung

In diesem kurzen Tutorial werdenwe’ll demonstrate how to merge two maps using the Java 8 capabilities.

Um genauer zu sein, werden wir verschiedene Zusammenführungsszenarien untersuchen, einschließlich Karten mit doppelten Einträgen.

2. Initialisierung

Definieren wir zunächst zweiMap-Instanzen:

private static Map map1 = new HashMap<>();
private static Map map2 = new HashMap<>();

Die KlasseEmployeeieht folgendermaßen aus:

public class Employee {
 
    private Long id;
    private String name;
 
    // constructor, getters, setters
}

Dann können wir einige Daten in die Instanzen vonMapverschieben:

Employee employee1 = new Employee(1L, "Henry");
map1.put(employee1.getName(), employee1);
Employee employee2 = new Employee(22L, "Annie");
map1.put(employee2.getName(), employee2);
Employee employee3 = new Employee(8L, "John");
map1.put(employee3.getName(), employee3);

Employee employee4 = new Employee(2L, "George");
map2.put(employee4.getName(), employee4);
Employee employee5 = new Employee(3L, "Henry");
map2.put(employee5.getName(), employee5);

Beachten Sie, dass wir identische Schlüssel füremployee1 undemployee5 Einträge in unseren Karten haben, die wir später verwenden werden.

3. Map.merge()

Java 8 adds a new merge() function into the java.util.Map interface.

So funktioniert die Funktionmerge(): Wenn der angegebene Schlüssel noch keinem Wert zugeordnet ist oder der Wert null ist, wird der Schlüssel dem angegebenen Wert zugeordnet.

Andernfalls wird der Wert durch die Ergebnisse der angegebenen Neuzuordnungsfunktion ersetzt. Wenn das Ergebnis der Neuzuordnungsfunktion null ist, wird das Ergebnis entfernt.

Zuerst erstellen wir ein neuesHashMap, indem wir alle Einträge aus demmap1 kopieren:

Map map3 = new HashMap<>(map1);

Als nächstes führen wir diemerge()-Funktion zusammen mit der Zusammenführungsregel ein:

map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())

Schließlich werden wir diemap2 durchlaufen und die Einträge inmap3 zusammenführen:

map2.forEach(
  (key, value) -> map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())));

Lassen Sie uns das Programm ausführen und den Inhalt vonmap3 drucken:

John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
George=Employee{id=2, name='George'}
Henry=Employee{id=1, name='Henry'}

Infolgedessen istour combined Map has all the elements of the previous HashMap entries. Entries with duplicate keys have been merged into one entry.

Außerdem stellen wir fest, dass dasEmployee-Objekt des letzten Eintragsid ausmap1 hat und der Wert ausmap2 ausgewählt wird.

Dies liegt an der Regel, die wir in unserer Fusionsfunktion definiert haben:

(v1, v2) -> new Employee(v1.getId(), v2.getName())

4. Stream.concat()

DieStream API in Java 8 kann auch eine einfache Lösung für unser Problem bieten. Erstenswe need to combine our Map instances into one Stream. Genau das macht die Operation vonStream.concat():

Stream combined = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream());

Hier übergeben wir die Karteneintragssätze als Parameter. Next, we need to collect our result into a new Map. Dafür können wirCollectors.toMap() verwenden:

Map result = combined.collect(
  Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Infolgedessen verwendet der Sammler die vorhandenen Schlüssel und Werte unserer Karten. Diese Lösung ist jedoch alles andere als perfekt. Sobald unser Sammler Einträge mit doppelten Schlüsseln trifft, wirft er einIllegalStateException.

Um dieses Problem zu lösen, fügen wir unserem Collector einfach einen dritten Lambda-Parameter "merger" hinzu:

(value1, value2) -> new Employee(value2.getId(), value1.getName())

Der Lambda-Ausdruck wird jedes Mal verwendet, wenn ein doppelter Schlüssel erkannt wird.

Zum Schluss fassen wir alles zusammen:

Map result = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
  .collect(Collectors.toMap(
    Map.Entry::getKey,
    Map.Entry::getValue,
    (value1, value2) -> new Employee(value2.getId(), value1.getName())));

Lassen Sie uns abschließend den Code ausführen und die Ergebnisse anzeigen:

George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
Henry=Employee{id=3, name='Henry'}

Wie wir sehen, wurden die doppelten Einträge mit dem Schlüssel“Henry” zu einem neuen Schlüssel-Wert-Paar zusammengeführt, wobeithe id of the new Employee was picked from the map2 and the value from map1.

5. Stream.of()

Um dieStream-API weiterhin zu verwenden, können wir unsereMap-Instanzen mithilfe vonStream.of() in einen einheitlichen Stream verwandeln.

Hier müssen wir keine zusätzliche Sammlung erstellen, um mit den Streams arbeiten zu können:

Map map3 = Stream.of(map1, map2)
  .flatMap(map -> map.entrySet().stream())
  .collect(Collectors.toMap(
    Map.Entry::getKey,
    Map.Entry::getValue,
    (v1, v2) -> new Employee(v1.getId(), v2.getName())));

Erstenswe transform map1 and map2 into a single stream. Als nächstes konvertieren wir den Stream in die Karte. Wie wir sehen können, ist das letzte Argument vontoMap() eine Zusammenführungsfunktion. Es löst das Problem der doppelten Schlüssel, indem das ID-Feld ausv1 Eintrag und der Name ausv2 ausgewählt werden.

Die gedrucktemap3-Instanz nach dem Ausführen des Programms:

George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
Henry=Employee{id=1, name='Henry'}

6. Einfaches Streaming

Zusätzlich können wir einestream() -Spipeline verwenden, um unsere Karteneinträge zusammenzustellen. Das folgende Codefragment zeigt, wie Sie die Einträge ausmap2 undmap1 hinzufügen, indem Sie die doppelten Einträge ignorieren:

Map map3 = map2.entrySet()
  .stream()
  .collect(Collectors.toMap(
    Map.Entry::getKey,
    Map.Entry::getValue,
    (v1, v2) -> new Employee(v1.getId(), v2.getName()),
  () -> new HashMap<>(map1)));

Wie erwartet sind die Ergebnisse nach dem Zusammenführen:

{John=Employee{id=8, name='John'},
Annie=Employee{id=22, name='Annie'},
George=Employee{id=2, name='George'},
Henry=Employee{id=1, name='Henry'}}

7. StreamEx

Zusätzlich zu den vom JDK bereitgestellten Lösungen können wir auch die beliebteStreamEx -Bibliothek verwenden.

Einfach ausgedrückt,StreamEx is an enhancement for the Stream API und bietet viele zusätzliche nützliche Methoden. Wir verwenden einEntryStream instance to operate on key-value pairs:

Map map3 = EntryStream.of(map1)
  .append(EntryStream.of(map2))
  .toMap((e1, e2) -> e1);

Die Idee ist, die Ströme unserer Karten in einem zusammenzuführen. Dann sammeln wir die Einträge in der Instanz des neuenmap3. Es ist wichtig zu erwähnen, dass der Ausdruck(e1, e2) → e1hilfreich ist, um die Regel für den Umgang mit den doppelten Schlüsseln zu definieren. Ohne sie wirft unser Code einIllegalStateException.

Und jetzt die Ergebnisse:

{George=Employee{id=2, name='George'},
John=Employee{id=8, name='John'},
Annie=Employee{id=22, name='Annie'},
Henry=Employee{id=1, name='Henry'}}

8. Zusammenfassung

In diesem kurzen Artikel haben wir verschiedene Möglichkeiten zum Zusammenführen von Karten in Java 8 kennengelernt. Insbesonderewe used Map.merge(), Stream API, StreamEx library.

Wie immer kann der während der Diskussion verwendete Codeover on GitHub gefunden werden.