Java Collect(): So nutzen Sie Reduktionsoperationen

Stream Collectors sind ein leistungsstarkes Feature der Java 8 Stream API, mit dem Sie Daten effizient sammeln und verarbeiten können. Wir erklären Ihnen den Aufbau und die Einsatzmöglichkeiten der Java-Collect-Methode.

Was sind die Anwendungsbereiche für Java Collect()?

Ein Stream Collector kann verwendet werden, um eine Liste, ein Set oder eine Map aus einem Stream zu erstellen. Ein Stream ist eine Sequenz von Elementen, die nacheinander verarbeitet werden. Die Collector-Schnittstelle bietet eine Reihe von Reduktionsoperationen für Daten in einer Stream-Pipeline. Es handelt sich hierbei um Terminaloperationen, die die Ergebnisse von Zwischenschritten sammeln und zusammenführen.

Collectors können beispielsweise genutzt werden, um Objekte aus einem Stream zu filtern oder zu sortieren. Auch Aggregation ist möglich, etwa das Summieren von Zahlen, Zusammenfassen von Strings oder Zählen von Elementen. Des Weiteren besitzen Collectors Funktionen, um die Inhalte eines Streams in eine bestimmte Struktur zu transformieren. So können Sie beispielsweise eine Liste in eine Map umwandeln. Gruppierungen helfen, Elemente mit bestimmten Eigenschaften oder Bedingungen zu kategorisieren. Vor allem aber haben Stream Collectors den Vorteil, dass sie Daten mithilfe mehrerer Threads parallel verarbeiten. Dadurch können Operationen, insbesondere bei großen Datenmengen, deutlich schneller und effizienter durchgeführt werden.

Das ist die Syntax von Java Collect()

Die Methode akzeptiert einen Collector als Argument, der beschreibt, wie die Elemente des Streams gesammelt und aggregiert werden sollen. Ein Collector ist ein Interface, das verschiedene Methoden bereitstellt, um die Elemente des Streams in eine bestimmte Form zusammenzuführen – z. B. in eine Liste, ein Set oder eine Map.

Es gibt zwei Arten der Java-Collect-Methode:

  1. <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner)
  2. <R, A> R collect(Collector<? super T, A, R> collector)

Die erste Variante hat als Argument drei Funktionen:

  • supplier: Erstellt einen Container, der für die Zwischenergebnisse verwendet wird.
  • accumulator: Berechnet das Endergebnis.
  • combiner: Kombiniert die Ergebnisse von parallelen Stream-Operationen.

Diese vordefinierten Collectors sind bereits in der Standardbibliothek enthalten und können einfach importiert und verwendet werden.

Die zweite Variante nimmt als Argument einen Collector und gibt das Ergebnis zurück.

  • R: der Typ des Ergebnisses
  • T: der Typ der Elemente im Stream
  • A: der Typ des Accumulators, der den Zwischenzustand der Collector-Operation speichert
  • collector: Führt die Reduktionsoperation aus.

Durch die Verwendung dieser Variante können Entwickler und Entwicklerinnen maßgeschneiderte Collectors erstellen, die speziell auf ihre Anforderungen zugeschnitten sind und eine höhere Flexibilität und Kontrolle über den Reduktionsprozess bieten.

Praktische Beispiele für den Einsatz von Java Collect()

Im Folgenden illustrieren wir verschiedene Funktionen der Stream.collect()-Methode. Sie sollten bereits mit den grundlegenden Java-Operatoren vertraut sein, bevor Sie in das Collection Framework einsteigen.

Eine Liste von Strings verketten

Mit Java Collect() können wir eine Liste von Strings konkatenieren, um einen neuen String zu erhalten:

List<String> letters = List.of("a", "b", "c", "d", "e");
// without combiner function
StringBuilder result = letters.stream().collect(StringBuilder::new, (x, y) -> x.append(y),
		(a, b) -> a.append(",").append(b));
System.out.println(result.toString());
// with combiner function
StringBuilder result1 = letters.parallelStream().collect(StringBuilder::new, (x, y) -> x.append(y),
		(a, b) -> a.append(",").append(b));
System.out.println(result1.toString());
Java

Wir erhalten die Ausgabe:

abcde
a, b, c, d, e
Java

Bei der ersten Berechnung liegt nur eine StringBuilder-Instanz vor und es wurde keine Combiner-Funktion verwendet. Deshalb lautet das Ergebnis abcde.

Im zweiten Output erkennen wir, dass die Combiner-Funktion die StringBuilder-Instanzen zusammengeführt und sie mit einem Komma getrennt hat.

Elemente mit toList() in einer Liste sammeln

Wir können mit der Funktion filter() bestimmte Elemente einer Liste selektieren und sie in einer neuen Liste mit toList() speichern.

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7);
List<Integer> oddNumbers = numbers.stream().filter(x -> x % 2 != 0).collect(Collectors.toList());
System.out.println(oddNumbers);
Java

Die neue Liste enthält nun ausschließlich ungerade Zahlen:

[1, 3, 5, 7]
Java

Elemente mit toSet() in einem Set sammeln

Analog können wir aus ausgewählten Elementen ein neues Set erstellen. Die Reihenfolge darf in einem Set ungeordnet sein.

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7);
Set<Integer> evenNumbers = numbers.parallelStream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
System.out.println(evenNumbers);
Java

Dies führt zur Ausgabe:

[2, 4, 6]
Java

Elemente mit toMap() in einer Map sammeln

Eine Map in Verbindung mit Java Collect() ordnet jedem Schlüssel einen Wert zu.

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7);
Map<Integer, String> mapEvenNumbers = numbers.parallelStream().filter(x -> x % 2 == 0)
		.collect(Collectors.toMap(Function.identity(), x -> String.valueOf(x)));
System.out.println(mapEvenNumbers);
Java

In der Ausgabe sehen wir, dass der Input, der aus geraden Zahlen besteht, seinen identischen Werten zugeordnet wurde:

{2=2, 4=4, 6=6}
Java

Elemente mit joining() in einer Zeichenkette kombinieren

Die joining()-Methode fügt jedes Element im Stream in der Reihenfolge hinzu, in der sie vorkommen und verwendet ein Trennzeichen, um die Elemente zu trennen. Das Trennzeichen wird als Argument an joining() übergeben. Wenn kein Trennzeichen angegeben wird, benutzt joining() den leeren String "".

jshell> String result1 = Stream.of("a", "b", "c").collect(Collectors.joining());
jshell> String result2 = Stream.of("a", "b", "c").collect(Collectors.joining(",", "{", "}"));
Java

Die Ergebnisse lauten:

result1 ==> "abc"
result2 ==> "{a,b,c}"
Java