Java 8 Stream und Collectors.toMap zum Erstellen einer Map verwenden

Java 8 bietet mit der neuen Stream API eine neue Möglichkeit, um mit den Elementen einer Liste zu arbeiten. Diese vereinfachen die Verarbeitung von Listen und können den Code erheblich kürzen. Wie funktioniert nun so ein Stream?

Nehmen wir an, wir haben folgende Klasse Address:

class Address {
	public String city;
	public String name;
	
	public Address(String city, String name) {
		this.city = city;
		this.name = name;
	}

        // Getter and Setter
        // ...
}

Und weiters eine Liste von Adressen:

List<Address> addresses = new ArrayList<Address>();
addresses.add(new Address("Vienna", "Huber"));
addresses.add(new Address("Bratislava", "Fischer"));
addresses.add(new Address("Vienna", "Ramos"));
addresses.add(new Address("Barcelona", "Langer"));
addresses.add(new Address("Bratislava", "Shorty"));	
addresses.add(new Address("Vienna", "Winter"));

Wollen wir nun eine Liste aller Namen ausgeben, können wir das auf alte Weise folgendermaßen machen:

for (Address addr: addresses) {
	System.out.println(addr.getName());
}

Das gleiche mit der Stream-API würde dann so aussehen:

addresses.stream().forEach(new Consumer<Address>() {
	@Override
	public void accept(Address addr) {
		System.out.println(addr.getName());
	}
});

Nichts dazugewonnen? Sieht auf den ersten Blick eher sehr viel umständlicher aus. Erst in Kombination mit den Java8 Lambda-Ausdrücken können wir die Vorteile ausspielen:

addresses.stream().forEach(addr -> System.out.println(addr.getName()));

Sieht schon kompakter aus. Nun wollen wir eine Map erstellen, welche als Schlüssel die jeweilige Stadt hat und als Wert einen String mit den Namen aller in dieser Stadt lebenden Personen. Dies können wir mit der collect()-Funktion der Stream-API und einer Collectors-Klasse durchführen, welche ein toMap-Funktion anbietet:

Map<String, String> cityMap = addresses.stream().collect(Collectors.toMap(
	addr -> addr.getCity(),
	addr -> addr.getName(),
	(name1, name2) -> name1 + ", " + name2));

System.out.println(cityMap);

/* Ausgabe:
{Barcelona=Langer, Vienna=Huber, Ramos, Winter, Bratislava=Fischer, Shorty}
*/

Die collect-Funktion erwartet einen Collector, welcher definiert wie die einzelnen Elemente wieder zusammengesammelt werden sollen. Die Klasse java.util.stream.Collectors bietet hierfür eine Auswahl an verschiedenen Kollektoren an. Die Funktion toMap fasst die Elemente in einer Map zusammen, deren Schlüssel und Werte das Resultat zweier Funktionen sind, der keyMapper-Funktion und der valueMapper-Funktion:

public static <T,K,U> Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,
                                                    Function<? super T,? extends U> valueMapper,
                                                    BinaryOperator<U> mergeFunction)

Zusätzlich verwenden wir hier den 3., optionalen Parameter mergeFunction, welcher zum Einsatz kommt wenn es zu einem Schlüssel mehr als einen Wert geben sollte, was ja in einer Map nicht erlaubt ist. Diese mergeFunction bekommt 2 Parameter und muß diese dann zu einem Wert zusammenfügen. In unserem Beispiel hängen wir den neuen Namen einfach an die vorigen mit einem Beistrich getrennt an.

Schreibe einen Kommentar