In Java 8, qual è la differenza tra i metodi Stream.map()
e Stream.flatMap()
?
Antworten
Zu viele Anzeigen?.map ist für A -> B
Mapping
Stream.of("dog", "cat") // Stream von 2 Strings
.map(s -> s.length()) // Stream von 2 Integers: [3, 3]
es wandelt jedes Element A
in jedes Element B
um. Javadoc
.flatMap ist für A -> Stream< B>
Konkatenierung
Stream.of("dog", "cat") // Stream von 2 Strings
.flatMapToInt(s -> s.chars()) // Stream von 6 ints: [d, o, g, c, a, t]
es --1 wandelt jedes Element A
in Stream< B>
um und dann --2 konkateniert alle Streams zu einem (flachen) Stream. Javadoc
Hinweis 1: Obwohl das letzte Beispiel zu einem Stream von Primitiven (IntStream) anstelle eines Streams von Objekten (Stream) führt, veranschaulicht es dennoch die Idee des .flatMap
.
Hinweis 2: Trotz des Namens gibt die Methode String.chars() ints zurück. Daher wird die tatsächliche Sammlung sein: [100, 111, 103, 99, 97, 116]
, wobei 100
der Code für 'd'
ist, 111
der Code für 'o'
usw. Auch hier wird zur Veranschaulichung [d, o, g, c, a, t] präsentiert.
Die Funktion, die Sie an stream.map
übergeben, muss ein Objekt zurückgeben. Das bedeutet, dass jedes Objekt im Eingabestrom genau einem Objekt im Ausgabestrom entspricht.
Die Funktion, die Sie an stream.flatMap
übergeben, gibt für jedes Objekt einen Strom zurück. Das bedeutet, dass die Funktion für jedes Eingabeobjekt beliebig viele Objekte zurückgeben kann (einschließlich keines). Die resultierenden Ströme werden dann zu einem Ausgabestrom konkateniert.
Für eine Karte haben wir eine Liste von Elementen und eine (Funktion, Aktion) f so :
[a,b,c] f(x) => [f(a),f(b),f(c)]
Und für die flache Karte haben wir eine Liste von Elementen und wir haben eine (Funktion, Aktion) f und möchten, dass das Ergebnis abgeflacht wird :
[[a,b],[c,d,e]] f(x) =>[f(a),f(b),f(c),f(d),f(e)]
Ich habe das Gefühl, dass die meisten Antworten hier das einfache Problem überkomplizieren. Wenn Sie bereits verstehen, wie die map
funktioniert, sollte das ziemlich einfach zu erfassen sein.
Es gibt Fälle, in denen wir unerwünschte verschachtelte Strukturen bekommen können, wenn wir map()
verwenden. Die Methode flatMap()
wurde entwickelt, um dies zu überwinden, indem sie das Einwickeln vermeidet.
Beispiele:
1
List> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
.collect(Collectors.toList());
Wir können verschachtelte Listen vermeiden, indem wir flatMap
verwenden:
List result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
.flatMap(i -> i.stream())
.collect(Collectors.toList());
2
Optional> result = Optional.of(42)
.map(id -> findById(id));
Optional result = Optional.of(42)
.flatMap(id -> findById(id));
wo:
private Optional findById(Integer id)
map() und flatMap()
map()
Nimmt einfach eine Funktion als Lambda-Parameter, wobei T das Element und R das zurückgegebene Element ist, das mit T erstellt wird. Am Ende haben wir einen Stream mit Objekten vom Typ R. Ein einfaches Beispiel könnte sein:
Stream
.of(1,2,3,4,5)
.map(myInt -> "präfix_"+myInt)
.forEach(System.out::println);
Es nimmt einfach Elemente 1 bis 5 des Typs Integer
, verwendet jedes Element, um ein neues Element vom Typ String
mit dem Wert "prefix_"+integer_wert
zu erstellen und gibt es aus.
flatMap()
Es ist nützlich zu wissen, dass flatMap() eine Funktion F
annimmt, wobei
-
T ein Typ ist, von dem aus ein Stream erstellt werden kann. Es kann eine Liste (T.stream()), ein Array (Arrays.stream(someArray)), etc. sein.. alles, von dem aus ein Stream erstellt werden kann. Im unten stehenden Beispiel hat jeder Entwickler viele Sprachen, also ist dev. Languages eine Liste und wird einen Lambda-Parameter verwenden.
-
R ist der resultierende Stream, der mit T erstellt wird. Da wir viele Instanzen von T haben, werden wir natürlich viele Streams von R haben. Alle diese Streams vom Typ R werden jetzt zu einem einzigen 'flachen' Stream vom Typ R kombiniert.
Beispiel
Die Beispiele von Bachiri Taoufiq [siehe seine Antwort hier] 1 sind einfach und leicht zu verstehen. Nur zur Klarstellung, nehmen wir an, wir haben ein Team von Entwicklern:
dev_team = {dev_1,dev_2,dev_3}
, wobei jeder Entwickler viele Sprachen kennt:
dev_1 = {lang_a,lang_b,lang_c},
dev_2 = {lang_d},
dev_3 = {lang_e,lang_f}
Mit Stream.map() auf dev_team angewendet, um die Sprachen jedes Entwicklers zu erhalten:
dev_team.map(dev -> dev.getLanguages())
erhalten Sie diese Struktur:
{
{lang_a,lang_b,lang_c},
{lang_d},
{lang_e,lang_f}
}
was im Grunde eine List> /Object[Sprachen[]]
ist. Nicht sehr hübsch, noch Java8-like!!
mit Stream.flatMap()
können Sie alles 'abflachen', da es die obige Struktur nimmt
und sie in {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}
umwandelt, was im Grunde als List/Sprache[]/etc
verwendet werden kann...
also am Ende würde Ihr Code mehr Sinn machen wie folgt:
dev_team
.stream() /* {dev_1,dev_2,dev_3} */
.map(dev -> dev.getLanguages()) /* {{lang_a,...,lang_c},{lang_d}{lang_e,lang_f}}} */
.flatMap(sprachen -> sprachen.stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
.tueWasAuchImStreamHier();
oder einfach:
dev_team
.stream() /* {dev_1,dev_2,dev_3} */
.flatMap(dev -> dev.getLanguages().stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
.tueWasAuchImStreamHier();
Wann map() und flatMap() verwenden:
-
Verwenden Sie
map()
, wenn jedes Element vom Typ T aus Ihrem Stream auf ein einzelnens Element vom Typ R abgebildet/umgewandelt werden soll. Das Ergebnis ist ein Mapping vom Typ (1 Startelement -> 1 Endelement) und es wird ein neuer Stream von Elementen vom Typ R zurückgegeben. -
Verwenden Sie
flatMap()
, wenn jedes Element vom Typ T aus Ihrem Stream auf eine Sammlung von Elementen vom Typ R abgebildet/umgewandelt werden soll. Das Ergebnis ist ein Mapping vom Typ (1 Startelement -> n Endelemente). Diese Sammlungen werden dann zusammengeführt (oder abgeflacht) zu einem neuen Stream von Elementen vom Typ R. Dies ist nützlich, um z.B. verschachtelte Schleifen darzustellen.
Vor Java 8:
List meineFoos = new ArrayList();
for(Foo foo: meineFoos){
for(Bar bar: foo.getMyBars()){
System.out.println(bar.getMyName());
}
}
Nach Java 8
meineFoos
.stream()
.flatMap(foo -> foo.getMyBars().stream())
.forEach(bar -> System.out.println(bar.getMyName()));