Streams: Vereinigen
Hat man zwei Listen von Objekten und möchte diese zusammen in einem Stream verarbeiten, kann man alle
Objekte in eine gemeinsame Liste stopfen und diese dann ver-streamen. Das ist selten elegant und oft nicht
möglich. Besser wär's, zwei Streams zu erzeugen, diese zusammenzuführen und dann das Ergebnis wie einen
einzigen Stream zu behandeln.
Das geht ganz einfach mit der – statischem – Methode Streams.concat()
.
In diesem Beispiel wird ein Stream aus dem Elementen eines List
-Objekts und eines Arrays gebildet:
List<String> list = Arrays.asList("a", "b", "c", "d"); String[] array = {"e", "f", "g", "h"}; Stream<String> strom = Stream.concat(list.stream(), Stream.of(array));
Seltsamerweise hat concat()
nur zwei Parameter. Um drei Listen zu vereinen, muß man concat()
zweimal ineinanderschachteln. Hier fügen wir noch einen dritten Stream hinzu:
List<String> list = Arrays.asList("a", "b", "c", "d"); String[] array = { "e", "f", "g", "h" }; Stream<String> more = Stream.of("i", "j", "k", "l"); Stream<String> strom = Stream.concat(Stream.concat(list.stream(), Stream.of(array)), more);
Man kann diese Einschränkung bei Bedarf mit einer selbstgeschriebenen Methode umgehen.
private <T> Stream<T> concat(Stream<T>... streams) { Stream<T> result = Stream.empty(); for (Stream<T> s : streams) { result = Stream.concat(result, s); } return result; }
Diese Implementierung erfüllt ihren Zweck, ist aber nicht besonders elegant (nicht zuletzt wegen der häßlichen
for-Schleife). Eine sehr viel schönere Lösung wird in der Beschreibung der reduce
-Methode gezeigt.
concat()
arbeitet mit Streams, also müssen zunächst aus List
-Objekt und Array je ein Stream generiert werden.
Diese werden dann vereint. Gehen wir wieder einen Schritt zurück und betrachten die einzelnen Objekte eines
Stroms von Objekten.
Ströme verbreitern: flatMap()
Hat man nur Objekte eines einzigen Typs – nicht wie im concat-Beispiel oben, das List
und Array
zusammenbrachte – kann man daraus einen Strom formen.
Mit der Stream-Methode flatMap()
kann man die einzelnen Objekte des Stroms – analog zur Methode map()
– verarbeiten. Sie akzeptiert als Eingabe ein Objekt vom Typ des Eingabe-Stroms und liefert als Ergebnis einen Strom von Objekten. Alle dabei entstehenden Ströme werden zu einem
einzigen Strom zusammengefaßt. Sehen wir uns das an einem Beispiel an:
List<String> list1 = Arrays.asList("a", "b", "c", "d"); List<String> list2 = Arrays.asList( "e", "f", "g", "h" ); List<String> list3 = Arrays.asList("i", "j", "k", "l"); Stream.of(list1, list2, list3) // .flatMap(List::stream) // .forEach(System.out::print);
- Zunächst werden drei
List
-Objekte erzeugt (list1
,list2
undlist3
). - Aus diesen wird mit
Stream.of()
einStream
-Objekt erzeugt. Der Typ des Streams istStream<List<String>>
- Es entsteht also ein Strom, der String-Listen als Elemente enthält.
- Auf jede Liste im Stream wird nun mit
flatMap()
die MethodeList.stream()
angewandt.- Aus jedem
List
-Objekt wird nun ein Strom vonString
-Objekten die zu einem einzigen Strom von Strings vereinigt werden.
- Aus jedem
- Über
foreach()
wird schließlich jedes Objekt des Stroms ausgegeben. Das Ergebnis lautet"abcdefghijkl"
Die Methode flatMap()
ist nicht auf Collections beschränkt. Tatsächlich kann man damit aus jedem Objekt einen Stream machen, wenn man nur die richtige Funktion baut. Hier machen wir aus Strings noch mehr Strings:
Stream.of("123", "456", "789") // .map(s -> s.split("")) // .flatMap(Stream::of) // .forEach(s->System.out.printf("[%s]", s));
- Wir beginnen mit einem Stream von drei Strings
- Wir zerlegen mit
map()
undsplit()
jeden String in ein Array von Strings, von dem jedes Element ein Zeichen enthält. - Dann machen wir mit
flatMap()
aus jedem Array einen Steam von Strings und vereinen die daraus entstehenden Ströme zu einem einzigen Strom. - Schließlich geben wir die einzelnen Zeichen mit umgebenden eckigen Klammern aus.