Was ist der Unterschied zwischen map () - und flatMap () -Methoden in Java 8?


706

Was ist in Java 8 der Unterschied zwischen Stream.map()und Stream.flatMap()Methoden?


55
Die Typensignatur erzählt irgendwie die ganze Geschichte. map :: Stream T -> (T -> R) -> Stream R, flatMap :: Stream T -> (T -> Stream R) -> Stream R.
Chris Martin

98
fwiw, diese Typensignaturen sehen nicht einmal wie Java aus. (Ich weiß, ich weiß - aber um zu sagen, es erzählt "die ganze Geschichte" von map / flatMap setzt viel Wissen über das neue und verbesserte "Java ++"
voraus

16
@michael Diese Typensignatur sieht aus wie Haskell, nicht wie Java. Es ist jedoch nicht klar, ob die tatsächliche Java-Signatur besser lesbar ist : <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper).
Stuart Marks

8
Ha, ja, ich bezog mich auf das "tatsächliche Java". Wie C ++ ist modernes Java für jeden, der es in den 90er Jahren verwendet hat, fast nicht wiederzuerkennen (wie ich, beide Sprachen). Wenn man nur auf den Kommentar antwortet, erzählen diese Methodensignaturen kaum eine "ganze Geschichte", zumindest nicht mehr, nicht ohne zusätzliche Darstellung (oder in diesem Fall des Kommentators Übersetzung).
Michael

2
Das heißt, mapdas Mapper-Lambda von a gibt zurück R, flatMapdas Mapper-Lambda von a gibt a Streamvon R( Stream<R>) zurück. Die vom flatMapMapper des Streams zurückgegebenen Streams werden effektiv verkettet. Ansonsten beides mapund flatMapzurück Stream<R>; der Unterschied ist , was die Mapper lambdas zurückkehren, Rgegen Stream<R>.
Derek

Antworten:


814

Beide mapund flatMapkönnen auf a angewendet werden Stream<T>und beide geben a zurück Stream<R>. Der Unterschied besteht darin, dass die mapOperation einen Ausgabewert für jeden Eingabewert erzeugt, während die flatMapOperation eine beliebige Anzahl (null oder mehr) für jeden Eingabewert erzeugt.

Dies spiegelt sich in den Argumenten für jede Operation wider.

Die mapOperation nimmt ein Function, das für jeden Wert im Eingabestream aufgerufen wird, und erzeugt einen Ergebniswert, der an den Ausgabestream gesendet wird.

Die flatMapOperation übernimmt eine Funktion, die konzeptionell einen Wert verbrauchen und eine beliebige Anzahl von Werten erzeugen möchte. In Java ist es jedoch umständlich, wenn 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 flatMapeinen Wert annimmt und ein Array oder ein Array zurückgibtListvon Werten, die dann an die Ausgabe gesendet werden. Da dies die Stream-Bibliothek ist, besteht eine besonders geeignete Möglichkeit, eine beliebige Anzahl von Rückgabewerten darzustellen, darin, dass die Mapper-Funktion selbst einen Stream zurückgibt! Die vom Mapper zurückgegebenen Werte aus dem Stream werden aus dem Stream abgelassen und an den Ausgabestream übergeben. Die "Klumpen" von Werten, die bei jedem Aufruf der Mapper-Funktion zurückgegeben werden, werden im Ausgabestream überhaupt nicht unterschieden, daher wird die Ausgabe als "abgeflacht" bezeichnet.

Typischerweise wird die Mapper-Funktion flatMapzurückgegeben, Stream.empty()wenn Nullwerte gesendet werden sollen, oder etwa, Stream.of(a, b, c)wenn mehrere Werte zurückgegeben werden sollen. Aber natürlich kann jeder Stream zurückgegeben werden.


26
Klingt für mich so, als wäre die flatMapOperation genau das Gegenteil von flach. Überlassen Sie es erneut den Informatikern, einen Begriff auf den Kopf zu stellen. Wie eine Funktion, die "transparent" ist, was bedeutet, dass Sie nichts sehen können, was sie tut, nur die Ergebnisse. Wenn Sie umgangssprachlich sagen, dass ein Prozess transparent sein soll, möchten Sie, dass jeder Teil davon gesehen wird.
Coladict

45
@coladict Versuchen Sie, es aus einer anderen Perspektive zu betrachten: Es ist kein transparenter Fall, in dem Sie das Innenleben durchschauen können, aber die gesamte Funktion selbst ist für Sie transparent, dh unsichtbar - während Sie ihre Arbeit erledigen und Sie sehen lassen, was Sie sind. Ich arbeite mit. In diesem Fall bezieht sich "flach" auf das Gegenteil von "verschachtelt". Flatmap entfernt eine Verschachtelungsebene durch Abflachen.
Zefiro

7
@coladict Das "transparente" Ding frisst seit Jahren meinen Kopf. Ich bin froh zu wissen, dass es mindestens einer anderen Person genauso geht.
Ashok Bijoy Debnath

9
Die Abflachung ergibt sich aus der Umwandlung einer 2-Ebenen-Struktur in eine einstufige Struktur. Siehe Dicis Antwort für ein Beispiel. Stackoverflow.com/a/26684582/6012102
andrzej.szmukala

26
Dies ist die beste Erklärung für flatMap . Dies ist der Grund, warum alles angeklickt wird: Die vom Mapper zurückgegebenen Werte aus dem Stream werden aus dem Stream entfernt und an den Ausgabestream übergeben. Die "Klumpen" von Werten, die bei jedem Aufruf der Mapper-Funktion zurückgegeben werden, werden im Ausgabestream überhaupt nicht unterschieden, daher wird die Ausgabe als "abgeflacht" bezeichnet . Vielen Dank!
Neevek

464

Stream.flatMap, wie der Name vermuten lässt, ist die Kombination von a mapund einer flatOperation. Das bedeutet, dass Sie zuerst eine Funktion auf Ihre Elemente anwenden und sie dann reduzieren. Stream.mapWendet nur eine Funktion auf den Stream an, ohne den Stream zu reduzieren.

Um zu verstehen, woraus das Abflachen eines Streams besteht, betrachten Sie eine Struktur [ [1,2,3],[4,5,6],[7,8,9] ]mit "zwei Ebenen". Abflachen bedeutet, es in eine "einstufige" Struktur umzuwandeln : [ 1,2,3,4,5,6,7,8,9 ].


5
einfach und süß
bluelurker

3
Haha, um fair zu sein, ich bin immer noch überrascht zu sehen, wie viel Verkehr diese Frage bekommt. Eine andere lustige Beobachtung ist, dass ich diese Antwort vor fast 5 Jahren geschrieben habe und es ein ziemlich konsistentes Muster der Abstimmung gibt, bei dem die akzeptierte Antwort ungefähr zwei positive Stimmen für jede meiner Antworten erhält. Es ist überraschend konsistent.
Dici

1
Wie ist das nicht die akzeptierte Antwort
?

233

Ich möchte zwei Beispiele geben , einen bekommen mehr praktischer Sicht:
Erstes Beispiel Herstellung Verwendung von Karte:

@Test
public void convertStringToUpperCaseStreams() {
    List<String> collected = Stream.of("a", "b", "hello") // Stream of String 
            .map(String::toUpperCase) // Returns a stream consisting of the results of applying the given function to the elements of this stream.
            .collect(Collectors.toList());
    assertEquals(asList("A", "B", "HELLO"), collected);
}   

Im ersten Beispiel Functionist nichts Besonderes. A wird angewendet, um den StringGroßbuchstaben zurückzugeben.

Zweites Beispiel unter Verwendung von flatMap:

@Test
public void testflatMap() throws Exception {
    List<Integer> together = Stream.of(asList(1, 2), asList(3, 4)) // Stream of List<Integer>
            .flatMap(List::stream)
            .map(integer -> integer + 1)
            .collect(Collectors.toList());
    assertEquals(asList(2, 3, 4, 5), together);
}

Im zweiten Beispiel wird ein Listenstrom übergeben. Es ist KEIN Stream von Integer!
Wenn eine Transformationsfunktion verwendet werden muss (über die Karte), muss der Stream zuerst auf etwas anderes reduziert werden (einen Stream of Integer).
Wenn flatMap entfernt wird, wird der folgende Fehler zurückgegeben: Der Operator + ist für die Argumenttypliste (n) undefiniert, int.
Es ist NICHT möglich, + 1 auf eine Liste von Ganzzahlen anzuwenden!


@PrashanthDebbadwar Ich denke, Sie würden Stream<Integer>eher einen Stream von als einen Stream von erhalten Integer.
Payne

166

Bitte gehen Sie den Beitrag vollständig durch, um eine klare Vorstellung zu bekommen.

map vs flatMap:

Um die Länge jedes Wortes aus einer Liste zurückzugeben, gehen wir wie folgt vor.

Kurzversion unten angegeben

Wenn wir zwei Listen sammeln, die unten angegeben sind

Ohne flache Karte => [1,2], [1,1] => [[1,2], [1,1]] Hier werden zwei Listen in eine Liste eingefügt, sodass die Ausgabe eine Liste mit Listen ist

Mit flacher Karte => [1,2], [1,1] => [1,2,1,1] Hier werden zwei Listen abgeflacht und nur die Werte in die Liste aufgenommen, sodass die Ausgabe eine Liste ist, die nur Elemente enthält

Grundsätzlich werden alle Objekte zu einem zusammengeführt

## Detaillierte Version wurde unten angegeben: -

Zum Beispiel: -
Betrachten Sie eine Liste ["STACK", "OOOVVVER"] und wir versuchen, eine Liste wie ["STACKOVER"] zurückzugeben (wobei nur eindeutige Buchstaben aus dieser Liste zurückgegeben werden) liste [“STACKOVER”] aus [“STACK”, ”OOOVVVER”]

public class WordMap {
  public static void main(String[] args) {
    List<String> lst = Arrays.asList("STACK","OOOVER");
    lst.stream().map(w->w.split("")).distinct().collect(Collectors.toList());
  }
}

Hier ist das Problem, dass Lambda, das an die Map-Methode übergeben wird, für jedes Wort ein String-Array zurückgibt. Der von der Map-Methode zurückgegebene Stream ist also tatsächlich vom Typ Stream. Was wir jedoch brauchen, ist Stream, um einen Stream von Zeichen darzustellen. Das folgende Bild zeigt das Problem.

Abbildung A:

Geben Sie hier die Bildbeschreibung ein

Sie könnten denken, dass wir dieses Problem mithilfe von Flatmap lösen können.
OK, lassen Sie uns sehen, wie Sie dieses Problem mithilfe von map und Arrays.stream lösen können. Zunächst benötigen Sie einen Zeichenstrom anstelle eines Strom von Arrays. Es gibt eine Methode namens Arrays.stream (), die ein Array verwendet und einen Stream erzeugt, zum Beispiel:

String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
    .map(Arrays::stream).distinct() //Make array in to separate stream
    .collect(Collectors.toList());

Das Obige funktioniert immer noch nicht, da wir jetzt eine Liste von Streams haben (genauer gesagt Stream>). Stattdessen müssen wir zuerst jedes Wort in ein Array einzelner Buchstaben konvertieren und dann jedes Array in einen separaten Stream verwandeln

Mit flatMap sollten wir dieses Problem wie folgt beheben können:

String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
    .flatMap(Arrays::stream).distinct() //flattens each generated stream in to a single stream
    .collect(Collectors.toList());

flatMap führt die Zuordnung jedes Arrays nicht zum Stream, sondern zum Inhalt dieses Streams durch. Alle einzelnen Streams, die bei Verwendung von map (Arrays :: stream) generiert werden, werden zu einem einzigen Stream zusammengeführt. Abbildung B zeigt den Effekt der Verwendung der flatMap-Methode. Vergleichen Sie es mit der Karte in Abbildung A. Abbildung B. Geben Sie hier die Bildbeschreibung ein

Mit der flatMap-Methode können Sie jeden Wert eines Streams durch einen anderen Stream ersetzen und dann alle generierten Streams zu einem einzigen Stream zusammenfügen.


2
Schöne schematische Erklärung.
Hitesh

107

Einzeilige Antwort: flatMapHilft, a Collection<Collection<T>>in a zu reduzierenCollection<T> . Auf die gleiche Weise wird auch ein Optional<Optional<T>>in abgeflacht Optional<T>.

Geben Sie hier die Bildbeschreibung ein

Wie Sie sehen können, mit map()nur:

  • Der Zwischentyp ist Stream<List<Item>>
  • Der Rückgabetyp ist List<List<Item>>

und mit flatMap():

  • Der Zwischentyp ist Stream<Item>
  • Der Rückgabetyp ist List<Item>

Dies ist das Testergebnis des unten verwendeten Codes:

-------- Without flatMap() -------------------------------
     collect() returns: [[Laptop, Phone], [Mouse, Keyboard]]

-------- With flatMap() ----------------------------------
     collect() returns: [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<String> items;

  public Parcel(String name, String... items) {
    this.name = name;
    this.items = Arrays.asList(items);
  }

  public List<String> getItems() {
    return items;
  }

  public static void main(String[] args) {
    Parcel amazon = new Parcel("amazon", "Laptop", "Phone");
    Parcel ebay = new Parcel("ebay", "Mouse", "Keyboard");
    List<Parcel> parcels = Arrays.asList(amazon, ebay);

    System.out.println("-------- Without flatMap() ---------------------------");
    List<List<String>> mapReturn = parcels.stream()
      .map(Parcel::getItems)
      .collect(Collectors.toList());
    System.out.println("\t collect() returns: " + mapReturn);

    System.out.println("\n-------- With flatMap() ------------------------------");
    List<String> flatMapReturn = parcels.stream()
      .map(Parcel::getItems)
      .flatMap(Collection::stream)
      .collect(Collectors.toList());
    System.out.println("\t collect() returns: " + flatMapReturn);
  }
}

8
Sehr klares Beispiel .., würde nicht mehr als ein paar Sekunden dauern, um das Konzept mit Ihrem Beispiel zu verstehen ...
TechDog

2
nette Erklärung. wirklich zu schätzen für einfache und beste Erklärung
Sachin Rane

42

Die Funktion, an die Sie übergeben, stream.mapmuss ein Objekt zurückgeben. Das bedeutet, dass jedes Objekt im Eingabestream genau ein Objekt im Ausgabestream ergibt.

Die Funktion, an die Sie übergeben, stream.flatMapgibt für jedes Objekt einen Stream zurück. Das bedeutet, dass die Funktion für jedes Eingabeobjekt eine beliebige Anzahl von Objekten zurückgeben kann (einschließlich keiner). Die resultierenden Streams werden dann zu einem Ausgabestream verkettet.


Warum sollten Sie "für jedes Eingabeobjekt eine beliebige Anzahl von Objekten zurückgeben (einschließlich keiner)"?
Derek Mahar

4
@DerekMahar Dafür gibt es viele Anwendungsfälle. Angenommen, Sie haben einen Stream von Departments in Ihrer Organisation. Jede Abteilung hat zwischen 0 und ns Employee. Was Sie brauchen, ist ein Strom aller Mitarbeiter. Also, was machst du? Sie schreiben eine flatMap-Methode, die eine Abteilung übernimmt und einen Strom ihrer Mitarbeiter zurückgibt.
Philipp

Philipp, veranschaulicht Ihr Beispiel den Hauptgrund für die Verwendung flatMap? Ich vermute, dass es zufällig sein kann und nicht den Hauptanwendungsfall oder den Grund dafür darstellt flatMap. (Fortsetzung unten ...)
Derek Mahar

Nach dem Lesen von dzone.com/articles/understanding-flatmap denke ich, dass die Hauptmotivation dahinter flatMapbesteht, Fehler zu berücksichtigen , die bei der Verwendung auftreten würden map. Wie gehen Sie mit Fällen um, in denen ein oder mehrere Elemente im Originalsatz keinem Ausgabeelement zugeordnet werden können? Durch die Einführung eines Zwischensatzes (z. B. ein Optionaloder Stream) für jedes Eingabeobjekt flatMapkönnen Sie die "ungültigen" Eingabeobjekte (oder die sogenannten "schlechten Äpfel" im Sinne von stackoverflow.com/a/52248643/107158 ) von der ausschließen letzter Satz.
Derek Mahar

1
@DerekMahar Ja, Studien, bei denen jedes Eingabeobjekt ein Ausgabeobjekt zurückgeben kann oder nicht, sind ein weiterer guter Anwendungsfall für Flat-Map.
Philipp

29

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 Liste und wir haben ein (Funktion, Aktion) f und wir wollen, dass das Ergebnis abgeflacht wird:

[[a,b],[c,d,e]] f(x) =>[f(a),f(b),f(c),f(d),f(e)]

25

Ich habe das Gefühl, dass die meisten Antworten hier das einfache Problem überkomplizieren. Wenn Sie bereits verstehen, wie die mapWerke funktionieren, sollte das ziemlich einfach zu verstehen sein.

Es gibt Fälle, in denen bei der Verwendung unerwünschte verschachtelte Strukturen auftreten map()können. Die flatMap()Methode wurde entwickelt, um dies zu vermeiden, indem das Umbrechen vermieden wird.


Beispiele:

1

List<List<Integer>> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .collect(Collectors.toList());

Wir können verschachtelte Listen vermeiden, indem wir Folgendes verwenden flatMap:

List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .flatMap(i -> i.stream())
  .collect(Collectors.toList());

2

Optional<Optional<String>> result = Optional.of(42)
      .map(id -> findById(id));

Optional<String> result = Optional.of(42)
      .flatMap(id -> findById(id));

wo:

private Optional<String> findById(Integer id)

Entschuldigung, aber das 2. Snippet von Punkt 1 wird nicht kompiliert List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(i -> i) .collect(Collectors.toList());. Es sollte seinStream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(List::stream) .collect(Collectors.toList());
Arthur

@arthur Ich denke, ich habe Vavrs Stream und Liste hier verwendet - aber ich stimme zu, dass es ein bisschen verwirrend sein könnte - ich werde das auf Standard Java ändern
Grzegorz Piwowarek


22

Der Artikel von Oracle über Optional hebt diesen Unterschied zwischen Karte und Flatmap hervor:

String version = computer.map(Computer::getSoundcard)
                  .map(Soundcard::getUSB)
                  .map(USB::getVersion)
                  .orElse("UNKNOWN");

Leider wird dieser Code nicht kompiliert. Warum? Der variable Computer ist vom Typ Optional<Computer>, daher ist es vollkommen richtig, die Kartenmethode aufzurufen. GetSoundcard () gibt jedoch ein Objekt vom Typ Optional zurück. Dies bedeutet, dass das Ergebnis der Kartenoperation ein Objekt vom Typ ist Optional<Optional<Soundcard>>. Infolgedessen ist der Aufruf von getUSB () ungültig, da das äußerste Optional als Wert ein anderes Optional enthält, das die Methode getUSB () natürlich nicht unterstützt.

Bei Streams verwendet die flatMap-Methode eine Funktion als Argument, die einen anderen Stream zurückgibt. Diese Funktion wird auf jedes Element eines Streams angewendet, was zu einem Stream von Streams führen würde. FlatMap hat jedoch den Effekt, dass jeder generierte Stream durch den Inhalt dieses Streams ersetzt wird. Mit anderen Worten, alle separaten Streams, die von der Funktion generiert werden, werden zu einem einzigen Stream zusammengeführt oder "abgeflacht". Was wir hier wollen, ist etwas Ähnliches, aber wir wollen eine zweistufige Option in eine "reduzieren" .

Optional wird auch eine flatMap-Methode unterstützt. Der Zweck besteht darin, die Transformationsfunktion auf den Wert einer Option anzuwenden (genau wie bei der Kartenoperation) und dann die resultierende Option mit zwei Ebenen in eine einzige zu reduzieren .

Um unseren Code korrekt zu machen, müssen wir ihn mit flatMap wie folgt umschreiben:

String version = computer.flatMap(Computer::getSoundcard)
                   .flatMap(Soundcard::getUSB)
                   .map(USB::getVersion)
                   .orElse("UNKNOWN");

Die erste flatMap stellt sicher, dass ein Optional<Soundcard>anstelle eines zurückgegeben wird Optional<Optional<Soundcard>>, und die zweite flatMap erfüllt den gleichen Zweck, ein zurückzugeben Optional<USB>. Beachten Sie, dass der dritte Aufruf nur eine map () sein muss, da getVersion () einen String anstelle eines optionalen Objekts zurückgibt.

http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html


1
Die Frage betraf Stream.map und Stream.flatMap und nicht Optional.map und Optional.flatMap
djames

4
Aber es hat mir sehr geholfen, meine Probleme mit optional und flatmap zu verstehen, vielen Dank!
Loïc

2
@djames, es ist eine vollkommen gültige Antwort, lesen Sie sie ab Absatz "Bei Streams nimmt die flatMap-Methode eine Funktion als Argument ..." :)
skwisgaar

Ich denke, dies ist eine sehr nützliche Ergänzung zu einigen der anderen Antworten hier.
Mz A

Die Version flatMap () löst auch eine nullpointerexception aus, wenn soundCard null ist. Wo ist also der versprochene Vorteil von Optional?
Ekaterina

16

Ich bin mir nicht sicher, ob ich darauf antworten soll, aber jedes Mal, wenn ich jemandem gegenüberstehe, der dies nicht versteht, verwende ich dasselbe Beispiel.

Stellen Sie sich vor, Sie haben einen Apfel. A mapwandelt diesen Apfel apple-juicebeispielsweise in eine Eins-zu-Eins- Zuordnung um.

Nehmen Sie denselben Apfel und holen Sie nur die Samen heraus, das ist es, was flatMaptut, oder eins zu viele , ein Apfel als Input, viele Samen als Output.


4
Das ist ein interessantes Beispiel :)
Cassiomolin

flatMapSammeln Sie für den Fall zuerst die Samen von jedem Apfel in separaten Beuteln, einem Beutel pro Apfel, bevor Sie alle Beutel in einen einzigen Beutel gießen?
Derek Mahar

@DerekMahar es war ein armer in einer einzigen Tasche vor Java-10, was bedeutet, flatmapwar nicht wirklich faul, aber seit Java-10 ist es faul
Eugene

@Eugene bitte erkläre ein etwas fauleres Konzept, das du mir nicht klar erklären willst. Ich habe verstanden, was derkerMahar im Kommentar erklärt hat, ist es das, was vor java10 passiert?
JAVA

@ JAVA nur suchen flatMap + lazy, ich wette, es wird einige Antworten geben.
Eugene

16

map () und flatMap ()

  1. map()

Nimmt einfach eine Funktion einen Lambda-Parameter, wobei T das Element und R das mit T erstellte Rückgabeelement ist. Am Ende haben wir einen Stream mit Objekten vom Typ R. Ein einfaches Beispiel kann sein:

Stream
  .of(1,2,3,4,5)
  .map(myInt -> "preFix_"+myInt)
  .forEach(System.out::println);

Es werden einfach die Elemente 1 bis 5 des Typs verwendet Integer, mit jedem Element ein neues Element aus dem Typ Stringmit Wert erstellt "prefix_"+integer_valueund ausgedruckt.

  1. flatMap()

Es ist nützlich zu wissen, dass flatMap () eine Funktion übernimmt, F<T, R>bei der

  • T ist ein Typ, aus dem ein Stream erstellt werden kann . Es kann eine Liste (T.stream ()), ein Array (Arrays.stream (someArray)) usw. sein, alles, was ein Stream mit / oder Form sein kann. Im folgenden Beispiel hat jeder Entwickler viele Sprachen, also Entwickler. Languages ​​ist eine Liste und verwendet einen Lambda-Parameter.

  • R ist der resultierende Stream, der mit T erstellt wird. Da wir wissen, dass wir viele Instanzen von T haben, werden wir natürlich viele Streams von R haben. Alle diese Streams von Typ R werden jetzt zu einem einzigen "flachen" Stream von Typ R kombiniert .

Beispiel

Die Beispiele von Bachiri Taoufiq , deren Antwort hier zu sehen ist, sind einfach und leicht zu verstehen. Nehmen wir zur Klarheit an, wir haben ein Entwicklerteam:

dev_team = {dev_1,dev_2,dev_3}

, wobei jeder Entwickler viele Sprachen beherrscht:

dev_1 = {lang_a,lang_b,lang_c},
dev_2 = {lang_d},
dev_2 = {lang_e,lang_f}

Anwenden von Stream.map () auf dev_team, um die Sprachen der einzelnen Entwickler abzurufen:

dev_team.map(dev -> dev.getLanguages())

gibt Ihnen diese Struktur:

{ 
  {lang_a,lang_b,lang_c},
  {lang_d},
  {lang_e,lang_f}
}

Das ist im Grunde ein List<List<Languages>> /Object[Languages[]]. Nicht so sehr hübsch, noch Java8-ähnlich !!

mit können Stream.flatMap()Sie Dinge "abflachen", während es die obige Struktur nimmt
und sie in verwandelt {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}, was im Grunde genommen als List<Languages>/Language[]/etc...

Am Ende wäre Ihr Code also sinnvoller:

dev_team
   .stream()    /* {dev_1,dev_2,dev_3} */
   .map(dev -> dev.getLanguages()) /* {{lang_a,...,lang_c},{lang_d}{lang_e,lang_f}}} */
   .flatMap(languages ->  languages.stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
   .doWhateverWithYourNewStreamHere();

oder einfach:

dev_team
       .stream()    /* {dev_1,dev_2,dev_3} */
       .flatMap(dev -> dev.getLanguages().stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
       .doWhateverWithYourNewStreamHere();

Wann man map () und flatMap () benutzt :

  • Verwenden Sie diese Option, map()wenn jedes Element vom Typ T aus Ihrem Stream einem einzelnen Element vom Typ R zugeordnet / transformiert werden soll . Das Ergebnis ist eine Zuordnung des Typs (1 Startelement -> 1 Endelement) und eines neuen Streams von Elementen des Typs R. ist zurück gekommen.

  • Verwenden Sie diese Option, flatMap()wenn jedes Element vom Typ T aus Ihrem Stream einer Sammlung von Elementen vom Typ R zugeordnet / transformiert werden soll . Das Ergebnis ist eine Zuordnung vom Typ (1 Startelement -> n Endelemente) . Diese Sammlungen werden dann zu einem neuen Strom von Elementen vom Typ R zusammengeführt (oder abgeflacht ). Dies ist beispielsweise nützlich, um verschachtelte Schleifen darzustellen .

Vor Java 8:

List<Foo> myFoos = new ArrayList<Foo>();
    for(Foo foo: myFoos){
        for(Bar bar:  foo.getMyBars()){
            System.out.println(bar.getMyName());
        }
    }

Post Java 8

myFoos
    .stream()
    .flatMap(foo -> foo.getMyBars().stream())
    .forEach(bar -> System.out.println(bar.getMyName()));

11

Map: - Diese Methode verwendet eine Funktion als Argument und gibt einen neuen Stream zurück, der aus den Ergebnissen besteht, die durch Anwenden der übergebenen Funktion auf alle Elemente des Streams generiert wurden.

Stellen wir uns vor, ich habe eine Liste von Ganzzahlwerten (1,2,3,4,5) und eine Funktionsschnittstelle, deren Logik das Quadrat der übergebenen Ganzzahl ist. (e -> e * e).

List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> newList = intList.stream().map( e -> e * e ).collect(Collectors.toList());

System.out.println(newList);

Ausgabe:-

[1, 4, 9, 16, 25]

Wie Sie sehen können, ist eine Ausgabe ein neuer Stream, dessen Werte ein Quadrat der Werte des Eingabestreams sind.

[1, 2, 3, 4, 5] -> apply e -> e * e -> [ 1*1, 2*2, 3*3, 4*4, 5*5 ] -> [1, 4, 9, 16, 25 ]

http://codedestine.com/java-8-stream-map-method/

FlatMap: - Diese Methode verwendet eine Funktion als Argument. Diese Funktion akzeptiert einen Parameter T als Eingabeargument und gibt einen Strom von Parameter R als Rückgabewert zurück. Wenn diese Funktion auf jedes Element dieses Streams angewendet wird, wird ein Stream mit neuen Werten erstellt. Alle Elemente dieser neuen Streams, die von jedem Element generiert werden, werden dann in einen neuen Stream kopiert, der ein Rückgabewert dieser Methode ist.

Stellen wir uns vor, ich habe eine Liste von Schülerobjekten, in denen jeder Schüler sich für mehrere Fächer entscheiden kann.

List<Student> studentList = new ArrayList<Student>();

  studentList.add(new Student("Robert","5st grade", Arrays.asList(new String[]{"history","math","geography"})));
  studentList.add(new Student("Martin","8st grade", Arrays.asList(new String[]{"economics","biology"})));
  studentList.add(new Student("Robert","9st grade", Arrays.asList(new String[]{"science","math"})));

  Set<Student> courses = studentList.stream().flatMap( e -> e.getCourse().stream()).collect(Collectors.toSet());

  System.out.println(courses);

Ausgabe:-

[economics, biology, geography, science, history, math]

Wie Sie sehen können, ist eine Ausgabe ein neuer Stream, dessen Werte eine Sammlung aller Elemente der Streams sind, die von jedem Element des Eingabestreams zurückgegeben werden.

[S1, S2, S3] -> [{"Geschichte", "Mathematik", "Geographie"}, {"Wirtschaft", "Biologie"}, {"Wissenschaft", "Mathematik"}] -> nehmen Sie einzigartige Fächer - > [Wirtschaft, Biologie, Geographie, Naturwissenschaften, Geschichte, Mathematik]

http://codedestine.com/java-8-stream-flatmap-method/


könnte einen Unterschied machen, wenn Sie Code bereitstellen, anstatt nur den Dokumentlink zu beweisen
Charles-Antoine Fournel

11

.map ist für A -> B Mapping

Stream.of("dog", "cat")              // stream of 2 Strings
    .map(s -> s.length())            // stream of 2 Integers: [3, 3]

Es konvertiert jedes Element Ain ein beliebiges Element B. Javadoc


.flatMap ist für A -> Stream <B> Concatinating

Stream.of("dog", "cat")             // stream of 2 Strings
    .flatMapToInt(s -> s.chars())   // stream of 6 ints:      [d, o, g, c, a, t]

es --1 konvertiert jedes Element Ain Stream< B>, dann --2 verkettet alle Streams zu einem (flachen) Stream. Javadoc


Anmerkung 1: Obwohl das letztere Beispiel auf einen Strom von Grundelementen (IntStream) anstatt auf einen Strom von Objekten (Stream) beschränkt ist, veranschaulicht es dennoch die Idee des .flatMap.

Anmerkung 2: Trotz des Namens gibt die String.chars () -Methode Ints zurück. Die eigentliche Sammlung lautet also: [100, 111, 103, 99, 97, 116] Wo 100ist der Code von 'd', 111ist der Code von 'o'usw. Zur Veranschaulichung wird er wiederum als [d, o, g, c, a, t] dargestellt.


3
Beste Antwort. Mit Beispielen auf den Punkt gebracht
GabrielBB

1

Einfache Antwort.

Die mapOperation kann eine Streamvon Stream.EX erzeugenStream<Stream<Integer>>

flatMapBetrieb wird nur Streamvon etwas produzieren. EXStream<Integer>


0

Eine gute Analogie kann auch mit C # sein, wenn Sie damit vertraut sind. Grundsätzlich C # Selectähnlich wie Java mapund C # SelectManyJava flatMap. Gleiches gilt für Kotlin für Sammlungen.


0

Dies ist für Anfänger sehr verwirrend. Der grundlegende Unterschied besteht darin map, dass für jeden Eintrag in der Liste ein Element ausgegeben wird und flatMapes sich im Grunde um eine map+ flattenOperation handelt. Um es klarer zu machen, verwenden Sie flatMap, wenn Sie mehr als einen Wert benötigen, z. B. wenn Sie erwarten, dass eine Schleife Arrays zurückgibt, ist flatMap in diesem Fall sehr hilfreich.

Ich habe einen Blog darüber geschrieben, den Sie hier nachlesen können .



0

Streamen Sie Operationen flatMapund mapakzeptieren Sie eine Funktion als Eingabe.

flatMaperwartet, dass die Funktion für jedes Element des Streams einen neuen Stream zurückgibt, und gibt einen Stream zurück, der alle Elemente der Streams kombiniert, die von der Funktion für jedes Element zurückgegeben werden. Mit anderen Worten, mit flatMapwerden für jedes Element aus der Quelle mehrere Elemente von der Funktion erstellt. http://www.zoftino.com/java-stream-examples#flatmap-operation

maperwartet, dass die Funktion einen transformierten Wert zurückgibt, und gibt einen neuen Stream zurück, der die transformierten Elemente enthält. Mit anderen Worten, mit mapwird für jedes Element aus der Quelle ein transformiertes Element von der Funktion erstellt. http://www.zoftino.com/java-stream-examples#map-operation


0

flatMap()nutzt auch die teilweise träge Auswertung von Streams. Es liest den ersten Stream und geht nur bei Bedarf zum nächsten Stream. Das Verhalten wird hier ausführlich erklärt: Ist flatMap garantiert faul?


0

Wenn Sie map()als Iteration (einstufige forSchleife) denken , flatmap()handelt es sich um eine zweistufige Iteration (wie eine verschachtelte forSchleife). (Geben Sie jedes iteratives Element foo, und tue foo.getBarList()und iterate, dass barListwieder)


map(): Nehmen Sie einen Stream, tun Sie etwas mit jedem Element, sammeln Sie das einzelne Ergebnis jedes Prozesses und geben Sie einen anderen Stream aus. Die Definition von "etwas tun" ist implizit. Wenn die Verarbeitung eines Elements dazu führt null, nullwird der endgültige Stream erstellt. Die Anzahl der Elemente im resultierenden Stream entspricht also der Anzahl der Eingabestreams.

flatmap(): Nehmen Sie einen Stream von Elementen / Streams und eine Funktion (explizite Definition), wenden Sie die Funktion auf jedes Element jedes Streams an und sammeln Sie alle resultierenden Zwischenströme, um einen größeren Stream zu erhalten ("Abflachung"). Wenn die Verarbeitung eines Elements dazu führt null, wird ein leerer Stream für den letzten Schritt des "Abflachens" bereitgestellt. Die Anzahl der Elemente im resultierenden Stream ist die Summe aller beteiligten Elemente in allen Eingaben, wenn es sich bei der Eingabe um mehrere Streams handelt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.