Qual è la differenza tra i metodi map () e flatMap () in Java 8?


706

In Java 8, qual è la differenza tra Stream.map() e Stream.flatMap()metodi?


55
Il tipo di firma in qualche modo racconta l'intera storia. map :: Stream T -> (T -> R) -> Stream R, flatMap :: Stream T -> (T -> Stream R) -> Stream R.
Chris Martin,

98
prima, quelle firme di tipo non assomigliano nemmeno a Java. (Lo so, lo so - ma per dire che racconta "l'intera storia" wrt map / flatMap presuppone molta conoscenza del nuovo e migliorato "Java ++")
michael

16
@michael Quel tipo di firma sembra Haskell, non Java. Ma non è chiaro se la firma effettiva Java è più leggibile: <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper).
Stuart segna il

8
Ah, sì, mi riferivo al "vero Java". Come il C ++, Java moderno è quasi irriconoscibile per chiunque abbia iniziato a usarlo negli anni '90 (come ho fatto io, entrambe le lingue). Solo rispondendo al commento, le firme di quel metodo difficilmente raccontano una "storia intera", almeno non più, non senza ulteriore esposizione (o in quel caso dei commentatori, la traduzione).
michael

2
Vale a dire, un mapmapper lambda di un restituisce R, un flatMapmapper lambda di restituisce un Streamdi R( Stream<R>). I flussi restituiti dal flatMapmapper del programma vengono effettivamente concatenati. Altrimenti, entrambi mape flatMapritorno Stream<R>; la differenza è ciò che restituiscono i lambda Mapper, Rcontro Stream<R>.
Derekm

Risposte:


816

Entrambi mape flatMappossono essere applicati a Stream<T>e entrambi restituiscono a Stream<R>. La differenza è che l' mapoperazione produce un valore di output per ciascun valore di input, mentre il valoreflatMap operazione produce un numero arbitrario (zero o più) valori per ciascun valore di input.

Ciò si riflette negli argomenti di ciascuna operazione.

L' mapoperazione richiede aFunction , che viene chiamato per ciascun valore nel flusso di input e produce un valore di risultato, che viene inviato al flusso di output.

IflatMap operazione assume una funzione che vuole concettualmente consumare un valore e produrre un numero arbitrario di valori. Tuttavia, in Java, è complicato per un metodo restituire un numero arbitrario di valori, poiché i metodi possono restituire solo zero o un valore. Si potrebbe immaginare un'API in cui la funzione mapper per flatMapassume un valore e restituisce un array o unListdi valori, che vengono quindi inviati all'output. Dato che questa è la libreria di flussi, un modo particolarmente adatto per rappresentare un numero arbitrario di valori di ritorno è che la funzione mapper stessa restituisca un flusso! I valori dallo stream restituiti dal mapper vengono scaricati dallo stream e passati al flusso di output. I "gruppi" di valori restituiti da ciascuna chiamata alla funzione mapper non si distinguono affatto nel flusso di output, quindi si dice che l'output sia stato "appiattito".

L'uso tipico è per la funzione mapper di flatMaprestituire Stream.empty()se vuole inviare valori zero, o qualcosa del genere Stream.of(a, b, c)se vuole restituire più valori. Ma ovviamente è possibile restituire qualsiasi flusso.


26
Mi sembra che l' flatMapoperazione sia esattamente l'opposto di flat. Ancora una volta, lascia agli scienziati informatici di girare un termine sulla sua testa. Come se una funzione fosse "trasparente", ciò significa che non puoi vedere nulla di ciò che fa, solo i risultati, mentre colloquialmente dire che vuoi che un processo sia trasparente significa che vuoi che ogni sua parte venga vista.
coladict

45
@coladict Prova a vederlo da una prospettiva diversa: non è un caso trasparente in cui puoi vedere i meccanismi interni, ma l'intera funzione stessa è trasparente, cioè invisibile, per te - mentre fai ancora il loro lavoro e ti fa vedere quello che ' sto lavorando con. In questo caso, "flat" si riferisce al contrario di "nidificato", flatmap rimuove un livello di annidamento mediante l'appiattimento.
Zefiro,

7
@coladict La cosa "trasparente" mi sta mangiando la testa da anni. Sono contento di sapere che almeno un'altra persona si sente allo stesso modo.
Ashok Bijoy Debnath,

9
Appiattimento viene da trasformare la struttura su 2 livelli nella struttura unico livello, vedere la risposta di Dici per un esempio stackoverflow.com/a/26684582/6012102
andrzej.szmukala

26
Questa è la migliore spiegazione di flatMap . Questo è ciò che fa clic su tutto: i valori dallo stream restituiti dal mapper vengono svuotati dallo stream e passati al flusso di output. I "gruppi" di valori restituiti da ciascuna chiamata alla funzione mapper non si distinguono affatto nel flusso di output, quindi si dice che l'output sia stato "appiattito" . Grazie!
Neevek,

464

Stream.flatMap, come si può intuire dal suo nome, è la combinazione di a mape flatun'operazione. Ciò significa che prima applichi una funzione ai tuoi elementi, quindi la appiattisci. Stream.mapapplica solo una funzione al flusso senza appiattire il flusso.

Per capire in cosa consiste l' appiattimento di un flusso, considera una struttura come quella [ [1,2,3],[4,5,6],[7,8,9] ]che ha "due livelli". Appiattimento questo mezzo trasformandola in una struttura "di un livello": [ 1,2,3,4,5,6,7,8,9 ].


5
simple and sweet
bluelurker

3
Haha, ad essere sinceri sono ancora sorpreso di vedere quanto traffico arriva a questa domanda. Un'altra osservazione divertente è che sono passati quasi 5 anni da quando ho scritto questa risposta e c'è stato un modello abbastanza coerente di votazione in cui la risposta accettata riceve circa due voti per ogni mia risposta. È sorprendentemente coerente.
Dici,

1
in che modo questa non è la risposta accettata, grazie per essere
venuti

233

Vorrei fare 2 esempi per ottenere un punto di vista più pratico:
Primo esempio facendo uso della mappa:

@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);
}   

Nulla di speciale nel primo esempio, a Functionviene applicato per restituire il Stringmaiuscolo.

Secondo esempio facendo uso di 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);
}

Nel secondo esempio, viene passato un flusso di elenco. NON è un flusso di numeri interi!
Se una funzione di trasformazione deve essere utilizzata (tramite la mappa), prima lo Stream deve essere appiattito a qualcos'altro (un flusso di numeri interi).
Se flatMap viene rimosso, viene restituito il seguente errore: L'operatore + non è definito per l'elenco dei tipi di argomento, int.
NON è possibile applicare + 1 su un elenco di numeri interi!


@PrashanthDebbadwar Penso che finiresti con uno Stream Stream<Integer>piuttosto che uno Stream Integer.
Payne,

166

Ti preghiamo di leggere attentamente il post per avere un'idea chiara,

map vs flatMap:

Per restituire una lunghezza di ogni parola da un elenco, faremmo qualcosa di simile di seguito.

Versione breve fornita di seguito

Quando raccogliamo due elenchi, riportati di seguito

Senza mappa piatta => [1,2], [1,1] => [[1,2], [1,1]] Qui due elenchi sono inseriti in un elenco, quindi l'output sarà un elenco contenente elenchi

Con flat map => [1,2], [1,1] => [1,2,1,1] Qui due elenchi sono appiattiti e solo i valori vengono inseriti nell'elenco, quindi l'output sarà un elenco contenente solo elementi

Fondamentalmente unisce tutti gli oggetti in uno

La versione dettagliata è stata fornita di seguito: -

Ad esempio: -
Considera un elenco ["STACK", "OOOVVVER"] e stiamo provando a restituire un elenco come ["STACKOVER"] (restituendo solo lettere univoche da tale elenco) Inizialmente, faremmo qualcosa di seguito per restituire un elenco ["STACKOVER"] da ["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());
  }
}

Qui il problema è che Lambda passato al metodo map restituisce un array String per ogni parola, quindi lo stream restituito dal metodo map è in realtà di tipo Stream, ma ciò di cui abbiamo bisogno è Stream per rappresentare un flusso di caratteri, l'immagine sotto mostra l'illustrazione problema.

Figura A:

inserisci qui la descrizione dell'immagine

Potresti pensare che, possiamo risolvere questo problema usando flatmap,
OK, vediamo come risolverlo usando map e Arrays.stream Prima di tutto avrai bisogno di un flusso di caratteri anziché di un flusso di matrici. Esiste un metodo chiamato Arrays.stream () che accetta un array e produce un flusso, ad esempio:

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());

Quanto sopra non funziona ancora, perché ora finiamo con un elenco di stream (più precisamente, Stream>), invece, dobbiamo prima convertire ogni parola in un array di singole lettere e quindi rendere ogni array in un flusso separato

Utilizzando flatMap dovremmo essere in grado di risolvere questo problema come di seguito:

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 eseguiva il mapping di ciascun array non con stream ma con i contenuti di tale stream. Tutti i singoli flussi che verrebbero generati durante l'utilizzo di map (Arrays :: stream) vengono uniti in un singolo stream. La Figura B illustra l'effetto dell'uso del metodo flatMap. Confrontalo con quello che fa la mappa nella figura A. Figura B inserisci qui la descrizione dell'immagine

Il metodo flatMap consente di sostituire ogni valore di un flusso con un altro flusso e quindi unisce tutti i flussi generati in un singolo flusso.


2
Bella spiegazione schematica.
Hitesh,

108

Risposta a una riga: flatMapaiuta a appiattire a Collection<Collection<T>>in aCollection<T> . Allo stesso modo, si appiattirà anche Optional<Optional<T>>in Optional<T>.

inserisci qui la descrizione dell'immagine

Come puoi vedere, con map()solo:

  • Il tipo intermedio è Stream<List<Item>>
  • Il tipo restituito è List<List<Item>>

e con flatMap():

  • Il tipo intermedio è Stream<Item>
  • Il tipo restituito è List<Item>

Questo è il risultato del test dal codice usato qui sotto:

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

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

Codice utilizzato :

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
Esempio molto nitido .., Non ci vorrebbe più di qualche secondo per capire il concetto con il tuo esempio ...
TechDog

2
bella spiegazione. apprezzo molto per la spiegazione semplice e migliore
Sachin Rane,

42

La funzione a cui si passa stream.mapdeve restituire un oggetto. Ciò significa che ogni oggetto nel flusso di input genera esattamente un oggetto nel flusso di output.

La funzione a cui si passa stream.flatMaprestituisce un flusso per ciascun oggetto. Ciò significa che la funzione può restituire un numero qualsiasi di oggetti per ciascun oggetto di input (incluso nessuno). I flussi risultanti vengono quindi concatenati a un flusso di output.


Perché dovresti voler "restituire un numero qualsiasi di oggetti per ciascun oggetto di input (incluso nessuno)"?
Derek Mahar,

4
@DerekMahar Ci sarebbero molti casi d'uso per questo. Ad esempio, supponiamo che tu abbia un flusso di Departments nella tua organizzazione. Ogni dipartimento ha tra 0 e n Employees. Ciò di cui hai bisogno è un flusso di tutti i dipendenti. Allora cosa fai? Scrivi un metodo flatMap che prende un dipartimento e restituisce un flusso di suoi dipendenti.
Philipp,

Philipp, il tuo esempio illustra il motivo principale da utilizzare flatMap? Ho il sospetto che possa essere casuale e non illustra il caso d'uso chiave o il motivo per cui flatMapesiste. (Continua sotto ...)
Derek Mahar,

Dopo aver letto dzone.com/articles/understanding-flatmap , penso che la motivazione principale dietro flatMapsia quella di sistemare gli errori che sarebbero presenti durante l'uso map. Come gestite i casi in cui uno o più elementi nel set originale non possono essere associati a un elemento di output? Introducendo un set intermedio (diciamo un Optionalo Stream) per ciascun oggetto di input, flatMapè possibile escludere gli oggetti di input "non validi" (o le cosiddette "mele cattive" nello spirito di stackoverflow.com/a/52248643/107158 ) dal set finale.
Derek Mahar,

1
@DerekMahar Sì, le stuazioni in cui ogni oggetto di input potrebbe o non potrebbe restituire un oggetto di output è un altro buon caso d'uso per la mappa piatta.
Philipp,

29

per una mappa abbiamo un elenco di elementi e una (funzione, azione) f così:

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

e per la mappa piatta abbiamo un elenco di elementi elenco e abbiamo una (funzione, azione) f e vogliamo che il risultato sia appiattito:

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

25

Ho la sensazione che la maggior parte delle risposte qui esagerino con il semplice problema. Se hai già capito come mapfunziona dovrebbe essere abbastanza facile da capire.

Ci sono casi in cui possiamo finire con strutture nidificate indesiderate quando utilizziamo map(), il flatMap()metodo è progettato per superare questo evitando il wrapping.


Esempi:

1

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

Possiamo evitare di avere elenchi nidificati utilizzando 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));

dove:

private Optional<String> findById(Integer id)

scusate ma il secondo frammento dal punto 1 non viene compilato invece di List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(i -> i) .collect(Collectors.toList());. Dovrebbe essereStream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(List::stream) .collect(Collectors.toList());
Arthur

@arthur Penso di aver usato lo Stream and List di Vavr qui - ma sono d'accordo che potrebbe essere un po 'confuso - Lo cambierò in Java standard
Grzegorz Piwowarek


22

L'articolo di Oracle su Opzionale evidenzia questa differenza tra mappa e flatmap:

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

Sfortunatamente, questo codice non viene compilato. Perché? Il computer variabile è di tipo Optional<Computer>, quindi è perfettamente corretto chiamare il metodo map. Tuttavia, getSoundcard () restituisce un oggetto di tipo Facoltativo. Ciò significa che il risultato dell'operazione della mappa è un oggetto di tipo Optional<Optional<Soundcard>>. Di conseguenza, la chiamata a getUSB () non è valida perché l'Opzionale più esterno contiene come valore un altro Opzionale, che ovviamente non supporta il metodo getUSB ().

Con gli stream, il metodo flatMap accetta una funzione come argomento, che restituisce un altro flusso. Questa funzione viene applicata a ciascun elemento di un flusso, il che comporterebbe un flusso di flussi. Tuttavia, flatMap ha l'effetto di sostituire ogni flusso generato dal contenuto di quel flusso. In altre parole, tutti i flussi separati generati dalla funzione vengono riuniti o "appiattiti" in un singolo flusso. Ciò che vogliamo qui è qualcosa di simile, ma vogliamo "appiattire" un Opzionale a due livelli in uno .

Opzionale supporta anche un metodo flatMap. Il suo scopo è applicare la funzione di trasformazione sul valore di un Opzionale (proprio come l'operazione della mappa) e quindi appiattire l'Opzione a due livelli risultante in uno singolo .

Quindi, per rendere corretto il nostro codice, dobbiamo riscriverlo come segue usando flatMap:

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

La prima flatMap assicura che Optional<Soundcard>venga restituita una anziché una Optional<Optional<Soundcard>>, e la seconda flatMap raggiunge lo stesso scopo di restituire una Optional<USB>. Si noti che la terza chiamata deve essere solo una mappa () perché getVersion () restituisce una stringa anziché un oggetto opzionale.

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


1
la domanda era su Stream.map e Stream.flatMap e non su Optional.map anfd Optional.flatMap
djames

4
Ma mi ha aiutato molto a capire i miei problemi con la mappa opzionale e piatta, grazie mille!
Loïc,

2
@james, è una risposta perfettamente valida, leggila a partire dal paragrafo "Con gli stream, il metodo flatMap utilizza una funzione come argomento ..." :)
skwisgaar,

Penso che questa sia un'aggiunta molto utile ad alcune delle altre risposte qui.
Mz A

La versione flatMap () genera anche nullpointerexception se soundCard è null. Allora, qual è il vantaggio promesso di Opzionale?
Ekaterina,

16

Non sono sicuro di dover rispondere, ma ogni volta che incontro qualcuno che non lo capisce, uso lo stesso esempio.

Immagina di avere una mela. A mapsta trasformando quella mela in apple-juiceper esempio o in una mappatura individuale.

Prendi quella stessa mela e ne ricava solo i semi, ecco cosa flatMapfa, o uno a molti , una mela come input, molti semi come output.


4
Questo è un esempio interessante :)
cassiomolin

Nel flatMapcaso, raccogli prima i semi di ogni mela in sacchetti separati, un sacchetto per mela, prima di versare tutti i sacchetti in un singolo sacchetto?
Derek Mahar,

@DerekMahar era un povero in una singola borsa prima di java-10, il che significa che flatmapnon era davvero pigro, ma dal momento che java-10 è pigro
Eugene,

@Eugene, per favore, spiega un concetto un po 'più pigro che stai cercando di spiegare non chiaro a me. Ho capito cosa derkerMahar ha spiegato nel commento è che cosa sta succedendo prima di java10?
JAVA,

@JAVA basta cercare flatMap + lazy, scommetto che ci saranno alcune risposte.
Eugene,

16

map () e flatMap ()

  1. map()

Prende solo una funzione un parametro lambda in cui T è elemento e R l'elemento di ritorno creato usando T. Alla fine avremo un flusso con oggetti di tipo R. Un semplice esempio può essere:

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

Prende semplicemente gli elementi da 1 a 5 di Tipo Integer, usa ogni elemento per costruire un nuovo elemento dal tipo Stringcon valore "prefix_"+integer_valuee lo stampa.

  1. flatMap()

È utile sapere che flatMap () assume una funzione in F<T, R>cui

  • T è un tipo da cui è possibile creare uno stream da / con . Può essere un elenco (T.stream ()), un array (Arrays.stream (someArray)), ecc. Qualsiasi cosa da cui uno Stream può essere con / o modulo. nell'esempio seguente ogni sviluppatore ha molte lingue, quindi dev. Lingue è un elenco e utilizzerà un parametro lambda.

  • R è il flusso risultante che verrà creato usando T. Sapendo che abbiamo molti casi di T, avremo naturalmente molti flussi da R. Tutti questi flussi dal tipo R ora saranno combinati in un singolo flusso "piatto" dal tipo R .

Esempio

Gli esempi di Bachiri Taoufiq vedono la sua risposta qui sono semplici e facili da capire. Per chiarezza, diciamo solo che abbiamo un team di sviluppatori:

dev_team = {dev_1,dev_2,dev_3}

, con ogni sviluppatore che conosce molte lingue:

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

Applicando Stream.map () su dev_team per ottenere le lingue di ogni sviluppatore:

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

ti darà questa struttura:

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

che è fondamentalmente un List<List<Languages>> /Object[Languages[]]. Non molto carino, né simile a Java8 !!

con Stream.flatMap()te puoi 'appiattire' le cose mentre prende la struttura sopra
e la trasforma in {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}, che può fondamentalmente essere usata come List<Languages>/Language[]/etc...

quindi alla fine, il tuo codice avrebbe più senso in questo modo:

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();

o semplicemente:

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

Quando utilizzare map () e flatMap () :

  • Utilizzare map()quando si suppone che ogni elemento di tipo T del flusso sia mappato / trasformato in un singolo elemento di tipo R. Il risultato è una mappatura di tipo (1 elemento iniziale -> 1 elemento finale) e un nuovo flusso di elementi di tipo R viene restituito.

  • Utilizzare flatMap()quando si suppone che ogni elemento di tipo T del flusso sia mappato / trasformato in raccolte di elementi di tipo R. Il risultato è una mappatura di tipo (1 elemento iniziale -> n elementi finali) . Queste raccolte vengono quindi unite (o appiattite ) in un nuovo flusso di elementi di tipo R. Ciò è utile, ad esempio, per rappresentare cicli nidificati .

Pre Java 8:

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

Posta Java 8

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

11

Mappa: - Questo metodo utilizza una funzione come argomento e restituisce un nuovo flusso costituito dai risultati generati applicando la funzione passata a tutti gli elementi del flusso.

Immaginiamo, ho un elenco di valori interi (1,2,3,4,5) e un'interfaccia di funzione la cui logica è quadrata dell'intero passato. (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);

produzione:-

[1, 4, 9, 16, 25]

Come puoi vedere, un output è un nuovo flusso i cui valori sono quadrati di valori del flusso di input.

[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: - Questo metodo accetta una funzione come argomento, questa funzione accetta un parametro T come argomento di input e restituisce un flusso di parametro R come valore di ritorno. Quando questa funzione viene applicata a ciascun elemento di questo flusso, produce un flusso di nuovi valori. Tutti gli elementi di questi nuovi flussi generati da ciascun elemento vengono quindi copiati in un nuovo flusso, che sarà un valore di ritorno di questo metodo.

Immaginiamo, ho un elenco di oggetti studente, in cui ogni studente può optare per più materie.

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);

produzione:-

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

Come puoi vedere, un output è un nuovo stream i cui valori sono una raccolta di tutti gli elementi dei flussi restituiti da ciascun elemento del flusso di input.

[S1, S2, S3] -> [{"storia", "matematica", "geografia"}, {"economia", "biologia"}, {"scienza", "matematica"}] -> accetta argomenti unici - > [economia, biologia, geografia, scienza, storia, matematica]

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


potrebbe fare la differenza se fornisci il codice anziché semplicemente provare il collegamento doc
Charles-Antoine Fournel,

11

.map è per la mappatura A -> B.

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

converte qualsiasi oggetto Ain qualsiasi oggetto B. Javadoc


.flatMap è per 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]

--1 converte qualsiasi elemento Ain Stream< B>, quindi --2 concatena tutti i flussi in un flusso (piatto). Javadoc


Nota 1: Sebbene quest'ultimo esempio si adatti a un flusso di primitive (IntStream) anziché a un flusso di oggetti (Stream), illustra comunque l'idea di .flatMap.

Nota 2: nonostante il nome, il metodo String.chars () restituisce ints. Quindi la raccolta effettiva sarà:, [100, 111, 103, 99, 97, 116] dov'è 100il codice di 'd', 111è il codice di 'o'ecc. Ancora una volta, a scopo illustrativo, è presentato come [d, o, g, c, a, t].


3
Migliore risposta. Dritto al punto con esempi
GabrielBB,

1

Semplice risposta.

L' mapoperazione può produrre un valore Streamdi Stream.EXStream<Stream<Integer>>

flatMapl'operazione produrrà solo Streamqualcosa. EXStream<Integer>


0

Anche una buona analogia può essere con C # se hai familiarità. Fondamentalmente C # Selectsimile a Java mape C # SelectManyJava flatMap. Lo stesso vale per Kotlin per le collezioni.


0

Questo è molto confuso per i principianti. La differenza di base è che mapemette un elemento per ogni voce dell'elenco ed flatMapè sostanzialmente un'operazione map+ flatten. Per essere più chiari, usa flatMap quando hai bisogno di più di un valore, ad es. Quando ti aspetti che un loop restituisca array, flatMap sarà davvero utile in questo caso.

Ho scritto un blog su questo, puoi verificarlo qui .



0

Operazioni di streaming flatMape mapaccettare una funzione come input.

flatMapsi aspetta che la funzione restituisca un nuovo flusso per ogni elemento del flusso e restituisce un flusso che combina tutti gli elementi dei flussi restituiti dalla funzione per ciascun elemento. In altre parole, con flatMap, per ogni elemento dalla sorgente, verranno creati più elementi dalla funzione. http://www.zoftino.com/java-stream-examples#flatmap-operation

mapsi aspetta che la funzione restituisca un valore trasformato e restituisce un nuovo flusso contenente gli elementi trasformati. In altre parole, con map, per ogni elemento dalla sorgente, un elemento trasformato verrà creato dalla funzione. http://www.zoftino.com/java-stream-examples#map-operation


0

flatMap()sfrutta anche la valutazione parziale pigra dei flussi. Leggerà il pugno e solo quando richiesto, passerà al flusso successivo. Il comportamento è spiegato in dettaglio qui: flatMap è garantito per essere pigro?


0

Se pensi map()come un'iterazione (un forciclo di livello ), flatmap()è un'iterazione a due livelli (come un forciclo nidificato ). (Inserisci ogni elemento iterato foo, e fai foo.getBarList()e itera di barListnuovo in quello )


map(): prendere un flusso, fare qualcosa per ogni elemento, raccogliere il singolo risultato di ogni processo, produrre un altro flusso. La definizione di "fare qualcosa" è implicita. Se il risultato di qualsiasi elemento risulta null,null viene utilizzato per comporre il flusso finale. Pertanto, il numero di elementi nel flusso risultante sarà uguale al numero del flusso di input.

flatmap(): accetta un flusso di elementi / flussi e una funzione (definizione esplicita), applica la funzione a ciascun elemento di ciascun flusso e raccoglie tutto il flusso risultante intermedio in modo che diventi un flusso maggiore ("appiattimento"). Se risulta l'elaborazione di qualsiasi elemento null, viene fornito un flusso vuoto alla fase finale di "appiattimento". Il numero di elementi nel flusso risultante è il totale di tutti gli elementi partecipanti in tutti gli input, se l'input è costituito da più flussi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.