In Java 8, qual è la differenza tra i metodi Stream.map()
e Stream.flatMap()
?
Antworten
Zu viele Anzeigen?Sowohl map
als auch flatMap
können auf einem Stream
angewendet werden und sie geben beide einen Stream
zurück. Der Unterschied besteht darin, dass die map
-Operation für jeden Eingabewert einen Ausgabewert erzeugt, während die flatMap
-Operation eine beliebige Anzahl (null oder mehr) Werte für jeden Eingabewert erzeugt.
Dies spiegelt sich in den Argumenten jeder Operation wider.
Die map
-Operation nimmt eine Function
entgegen, die für jeden Wert im Eingabestream aufgerufen wird und einen Ergebniswert produziert, der an den Ausgabestream gesendet wird.
Die flatMap
-Operation nimmt eine Funktion entgegen, die konzeptionell einen Wert konsumieren möchte und eine beliebige Anzahl von Werten produzieren möchte. In Java ist es jedoch umständlich, dass eine Methode eine beliebige Anzahl von Werten zurückgibt, da Methoden nur null oder einen Wert zurückgeben können. Man könnte sich eine API vorstellen, bei der die Mapper-Funktion für flatMap
einen Wert entgegennimmt und ein Array oder eine List
von Werten zurückgibt, die dann an den Ausgang gesendet werden. Da es sich hier um die Streams-Bibliothek handelt, ist eine besonders passende Möglichkeit, eine beliebige Anzahl von Rückgabewerten darzustellen, dass die Mapper-Funktion selbst einen Stream zurückgibt! Die Werte aus dem vom Mapper zurückgegebenen Stream werden aus dem Stream entnommen und an den Ausgabestream weitergeleitet. Die "Klumpen" von Werten, die jeder Aufruf der Mapper-Funktion zurückgibt, werden im Ausgabestream überhaupt nicht unterschieden, daher wird gesagt, dass der Ausgang "flach gemacht" wurde.
Typischerweise gibt die Mapper-Funktion von flatMap
Stream.empty()
zurück, wenn sie null Werte senden möchte, oder etwas wie Stream.of(a, b, c)
, wenn sie mehrere Werte zurückgeben möchte. Aber natürlich kann jeder Stream zurückgegeben werden.
Stream.flatMap
, wie es sich bereits aus seinem Namen ableiten lässt, ist die Kombination einer map
und einer flat
Operation. Das bedeutet, dass Sie zunächst eine Funktion auf Ihre Elemente anwenden und sie dann zusammenführen. Stream.map
wendet nur eine Funktion auf den Stream an, ohne den Stream zusammenzuführen.
Um zu verstehen, was das "Zusammenführen" eines Streams bedeutet, nehmen Sie eine Struktur wie [ [1,2,3],[4,5,6],[7,8,9] ]
, die "zwei Ebenen" hat. Diese zu vereinen heißt, sie in eine "eine Ebene" Struktur zu transformieren: [ 1,2,3,4,5,6,7,8,9 ]
.
Ich würde gerne 2 Beispiele geben, um einen praktischeren Standpunkt zu bekommen:
Erstes Beispiel mit Verwendung von map
:
@Test
public void convertStringToUpperCaseStreams() {
List collected = Stream.of("a", "b", "hello") // Stream aus String
.map(String::toUpperCase) // Gibt einen Stream zurück, der aus den Ergebnissen besteht, die durch Anwendung der gegebenen Funktion auf die Elemente dieses Streams erzeugt werden.
.collect(Collectors.toList());
assertEquals(asList("A", "B", "HELLO"), collected);
}
Im ersten Beispiel ist nichts Besonderes, eine Function
wird angewendet, um den String
in Großbuchstaben zurückzugeben.
Zweites Beispiel mit Verwendung von flatMap
:
@Test
public void testflatMap() throws Exception {
List together = Stream.of(asList(1, 2), asList(3, 4)) // Stream aus List
.flatMap(List::stream)
.map(integer -> integer + 1)
.collect(Collectors.toList());
assertEquals(asList(2, 3, 4, 5), together);
}
Im zweiten Beispiel wird ein Stream von Listen übergeben. Es ist KEIN Stream von Integer!
Wenn eine Transformationsfunktion verwendet werden muss (über map), muss der Stream zunächst zu etwas anderem (ein Stream von Integer) abgeflacht werden.
Wenn flatMap
entfernt wird, wird der folgende Fehler zurückgegeben: Der Operator + ist für die Argumenttypen List, int nicht definiert.
Es ist NICHT möglich, + 1 auf eine List
von Integern anzuwenden!
Bitte lesen Sie den Beitrag vollständig durch, um eine klare Vorstellung zu bekommen,
Karte vs flatMap:
Um die Länge jedes Wortes aus einer Liste zurückzugeben, würden wir etwas Ähnliches tun wie unten..
Kurzversion unten gegeben
Wenn wir zwei Listen sammeln, unten angegeben
Ohne flatmap => [1,2],[1,1] => [[1,2],[1,1]] Hier werden zwei Listen in einer Liste platziert, daher wird die Ausgabe eine Liste enthaltend Listen sein
Mit flat map => [1,2],[1,1] => [1,2,1,1] Hier werden zwei Listen abgeflacht und nur die Werte werden in der Liste platziert, daher wird die Ausgabe eine Liste enthaltend nur Elemente sein
Grundsätzlich vereint es alle Objekte zu einem einzigen
## Ausführliche Version wurde unten gegeben:-
Zum Beispiel:-
Betrachten Sie eine Liste [“STACK”, ”OOOVVVER”] und wir versuchen eine Liste wie [“STACKOVER”] zurückzugeben (nur eindeutige Buchstaben aus dieser Liste zurückgeben) Anfangs würden wir etwas Ähnliches tun, um eine Liste [“STACKOVER”] von [“STACK”, ”OOOVVVER”] zurückzugeben
public class WordMap {
public static void main(String[] args) {
List lst = Arrays.asList("STACK","OOOVER");
lst.stream().map(w->w.split("")).distinct().collect(Collectors.toList());
}
}
Hier liegt das Problem darin, dass die Lambda, die dem map-Methoden übergeben wird, für jedes Wort eine Zeichenfolgenarray zurückgibt, daher ist der von der map-Methode zurückgegebene Stream tatsächlich vom Typ Stream, aber was wir benötigen, ist Stream, um einen Strom von Zeichen zu repräsentieren, das folgende Bild verdeutlicht das Problem.
Figur A:
Sie könnten denken, dass wir dieses Problem mit flatmap lösen können,
OK, schauen wir, wie wir dies mit map und Arrays.stream lösen können Zunächst benötigen Sie einen Strom von Zeichen anstelle eines Stroms von Arrays. Es gibt eine Methode namens Arrays.stream(), die ein Array akzeptiert und einen Stream erzeugt, zum Beispiel:
String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Wort in Array von Buchstaben umwandeln
.map(Arrays::stream).distinct() //Array in separaten Strom umwandeln
.collect(Collectors.toList());
Das funktioniert immer noch nicht, denn jetzt landen wir bei einer Liste von Strömen (genauer gesagt, Stream>), stattdessen müssen wir zuerst jedes Wort in ein Array von einzelnen Buchstaben umwandeln und dann jedes Array in einen separaten Strom umwandeln
Indem wir flatMap verwenden, sollten wir dieses Problem wie folgt lösen können:
String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Wort in Array von Buchstaben umwandeln
.flatMap(Arrays::stream).distinct() //jeden generierten Strom in einen einzigen Strom abflachen
.collect(Collectors.toList());
flatMap würde jedes Array nicht mit einem Strom, sondern mit den Inhalten dieses Stroms abbilden. Alle einzelnen Ströme, die während der Verwendung von map(Arrays::stream) generiert werden, werden zu einem einzelnen Strom zusammengeführt. Abbildung B veranschaulicht die Auswirkungen der Verwendung der flatMap-Methode. Vergleichen Sie dies mit dem, was map in Abbildung A tut. Figur B
Die flatMap-Methode ermöglicht es Ihnen, jeden Wert eines Stroms durch einen anderen Strom zu ersetzen und dann alle generierten Ströme in einen einzigen Strom zusammenzuführen.
Eine Zeile Antwort: flatMap
hilft dabei, eine Collection>
in eine Collection
zu flatten. Auf die gleiche Weise wird es auch ein Optional>
in ein Optional
flatten.
Wie Sie sehen können, nur mit map()
:
- Der Zwischentyp ist
Stream>
- Der Rückgabetyp ist
List>
und mit flatMap()
:
- Der Zwischentyp ist
Stream
- Der Rückgabetyp ist
List
Dies ist das Testergebnis des direkt darunter verwendeten Codes:
-------- Ohne flatMap() -------------------------------
collect() gibt zurück: [[Laptop, Phone], [Mouse, Keyboard]]
-------- Mit flatMap() ----------------------------------
collect() gibt zurück: [Laptop, Phone, Mouse, Keyboard]
Verwendeter Code:
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class Parcel {
String name;
List items;
public Parcel(String name, String... items) {
this.name = name;
this.items = Arrays.asList(items);
}
public List getItems() {
return items;
}
public static void main(String[] args) {
Parcel amazon = new Parcel("amazon", "Laptop", "Phone");
Parcel ebay = new Parcel("ebay", "Mouse", "Keyboard");
List parcels = Arrays.asList(amazon, ebay);
System.out.println("-------- Ohne flatMap() ---------------------------");
List> mapReturn = parcels.stream()
.map(Parcel::getItems)
.collect(Collectors.toList());
System.out.println("\t collect() gibt zurück: " + mapReturn);
System.out.println("\n-------- Mit flatMap() ------------------------------");
List flatMapReturn = parcels.stream()
.map(Parcel::getItems)
.flatMap(Collection::stream)
.collect(Collectors.toList());
System.out.println("\t collect() gibt zurück: " + flatMapReturn);
}
}
- See previous answers
- Weitere Antworten anzeigen