Zusammenführen von zwei Karten mit Java 8

1. Einleitung

In diesem kurzen Tutorial zeigen wir Ihnen, wie Sie zwei Karten mithilfe der Java 8-Funktionen zusammenführen .

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

2. Initialisierung

Definieren wir zunächst zwei Map- Instanzen:

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

Die Employee- Klasse sieht folgendermaßen aus:

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

Dann können wir einige Daten in die Map- Instanzen verschieben:

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ür Mitarbeiter1- und Mitarbeiter5- Einträge in unseren Karten haben, die wir später verwenden werden.

3. Map.merge ()

Java 8 fügt der Schnittstelle java.util.Map eine neue Funktion merge () hinzu .

So funktioniert die Funktion merge () : 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.

Lassen Sie uns zunächst eine neue HashMap erstellen, indem Sie alle Einträge von map1 kopieren :

Map map3 = new HashMap(map1);

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

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

Schließlich werden wir über map2 iterieren und die Einträge in map3 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 von map3 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 enthält unsere kombinierte Karte alle Elemente der vorherigen HashMap- Einträge. Einträge mit doppelten Schlüsseln wurden zu einem Eintrag zusammengeführt .

Außerdem stellen wir fest, dass das Employee- Objekt des letzten Eintrags die ID von map1 hat und der Wert von map2 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 ()

Die Stream- API in Java 8 bietet auch eine einfache Lösung für unser Problem. Zuerst müssen wir unsere Map- Instanzen zu einem Stream kombinieren . Genau das macht die Operation Stream.concat () :

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

Hier übergeben wir die Karteneintragssätze als Parameter. Als nächstes müssen wir unser Ergebnis in einer neuen Karte sammeln . Dafür können wir Collectors.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 Collector Einträge mit doppelten Schlüsseln trifft, wird eine IllegalStateException ausgelöst .

Um dieses Problem zu beheben, fügen wir unserem Kollektor 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 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 zum Schluss den Code ausführen und die Ergebnisse sehen:

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, wobei die ID des neuen Mitarbeiters aus map2 und der Wert aus map1 ausgewählt wurden .

5. Stream.of ()

Um die Stream- API weiterhin verwenden zu können, können wir unsere Map- Instanzen mithilfe von Stream.of () in einen einheitlichen Stream verwandeln .

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

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

Zuerst transformieren wir map1 und map2 in einen einzigen Stream . Als nächstes konvertieren wir den Stream in die Karte. Wie wir sehen können, ist das letzte Argument von toMap () eine Zusammenführungsfunktion. Es löst das Problem der doppelten Schlüssel, indem das ID-Feld aus dem Eintrag v1 und der Name aus v2 ausgewählt werden .

Die gedruckte map3- 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 eine stream () - Pipeline verwenden, um unsere Karteneinträge zusammenzustellen. Das folgende Codefragment zeigt, wie Sie die Einträge aus map2 und map1 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 wir erwarten, sind die Ergebnisse nach dem Zusammenschluss:

{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

In addition to solutions that are provided by the JDK, we can also use the popular StreamEx library.

Simply put, StreamEx is an enhancement for the Stream API and provides many additional useful methods. We'll use an EntryStream instance to operate on key-value pairs:

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

The idea is to merge the streams of our maps into one. Then we collect the entries into the new map3 instance. Important to mention, the (e1, e2) -> e1 expression as it helps to define the rule for dealing with the duplicate keys. Without it, our code will throw an IllegalStateException.

And now, the results:

{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. Summary

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

Wie immer ist der während der Diskussion verwendete Code auf GitHub zu finden.