Quelle est la différence entre les méthodes map () et flatMap () dans Java 8?

707

En Java 8, quelle est la différence entre les méthodes Stream.map()et Stream.flatMap()?

cassiomoline
la source
55
La signature de type raconte un peu toute l'histoire. map :: Stream T -> (T -> R) -> Stream R, flatMap :: Stream T -> (T -> Stream R) -> Stream R.
Chris Martin
99
fwiw, ces signatures de type ne ressemblent même pas à Java. (Je sais, je sais - mais dire que ça raconte "toute l'histoire" wrt map / flatMap suppose beaucoup de connaissances sur le nouveau et amélioré "Java ++")
michael
16
@michael Cette signature de type ressemble à Haskell, pas à Java. Mais on ne sait pas si la signature Java réelle est plus lisible: <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper).
Stuart marque le
8
Ha, ouais, je faisais allusion au "Java réel". Comme C ++, Java moderne est presque méconnaissable pour quiconque a commencé à l'utiliser dans les années 90 (comme je l'ai fait, les deux langues). Répondant simplement au commentaire, les signatures de cette méthode racontent à peine une "histoire entière", du moins pas plus, non sans exposition supplémentaire (ou dans ce cas, commentateurs, traduction).
michael
2
C'est-à-dire que maple mappeur lambda retourne R, flatMaple mappeur lambda renvoie a Streamde R( Stream<R>). Les flux renvoyés par le flatMapmappeur de sont effectivement concaténés. Sinon, les deux mapet le flatMapretour Stream<R>; la différence est ce que les lambdas mappeur retour, Rcontre Stream<R>.
derekm

Réponses:

816

Les deux mapet flatMappeuvent être appliqués à un Stream<T>et ils renvoient tous les deux un Stream<R>. La différence est que l' mapopération produit une valeur de sortie pour chaque valeur d'entrée, tandis queflatMap opération produit un nombre arbitraire (zéro ou plus) pour chaque valeur d'entrée.

Cela se reflète dans les arguments de chaque opération.

L' mapopération prendFunction , qui est appelé pour chaque valeur dans le flux d'entrée et produit une valeur de résultat, qui est envoyée au flux de sortie.

L' flatMapopération prend une fonction qui veut conceptuellement consommer une valeur et produire un nombre arbitraire de valeurs. Cependant, en Java, il est difficile pour une méthode de renvoyer un nombre arbitraire de valeurs, car les méthodes ne peuvent renvoyer que zéro ou une valeur. On pourrait imaginer une API où la fonction de mappage flatMapprend une valeur et retourne un tableau ou unListdes valeurs, qui sont ensuite envoyées à la sortie. Étant donné qu'il s'agit de la bibliothèque de flux, un moyen particulièrement approprié de représenter un nombre arbitraire de valeurs de retour est que la fonction de mappage elle-même retourne un flux! Les valeurs du flux renvoyé par le mappeur sont drainées du flux et transmises au flux de sortie. Les "blocs" de valeurs renvoyées par chaque appel à la fonction de mappage ne sont pas du tout distingués dans le flux de sortie, donc la sortie aurait été "aplatie".

L'utilisation typique est que la fonction de mappage de flatMapretourStream.empty() si elle veut envoyer des valeurs nulles, ou quelque chose comme Stream.of(a, b, c)si elle veut retourner plusieurs valeurs. Mais bien sûr, tout flux peut être retourné.

Marques Stuart
la source
26
Cela me semble que l' flatMapopération est exactement l'opposé de plat. Encore une fois, laissez aux informaticiens le soin de tourner la tête. Comme une fonction «transparente» signifiant que vous ne pouvez rien voir de ce qu'elle fait, juste les résultats, tout en disant familièrement que vous voulez qu'un processus soit transparent signifie que vous voulez que chaque partie de celui-ci soit vue.
coladict
45
@coladict Essayez de le voir sous un angle différent: ce n'est pas un cas transparent où vous pouvez voir le fonctionnement interne, mais toute la fonction elle-même est transparente, c'est-à-dire invisible pour vous - tout en faisant son travail et en vous laissant voir ce que vous '' re travailler avec. Dans ce cas, "plat" se réfère à l'opposé de "imbriqué", flatmap supprime un niveau d'imbrication en aplatissant.
Zefiro
7
@coladict La chose "transparente" me mange la tête depuis des années. Heureux de savoir qu'au moins une autre personne ressent la même chose.
Ashok Bijoy Debnath
9
L'aplatissement provient de la transformation d'une structure à 2 niveaux en structure à un seul niveau, voir la réponse de Dici pour un exemple stackoverflow.com/a/26684582/6012102
andrzej.szmukala
26
C'est la meilleure explication de flatMap . C'est ce qui fait que tout est cliqué: les valeurs du flux renvoyé par le mappeur sont drainées du flux et transmises au flux de sortie. Les "blocs" de valeurs renvoyées par chaque appel à la fonction de mappage ne sont pas du tout distingués dans le flux de sortie, donc la sortie est dite "aplatie" . Je vous remercie!
neevek
464

Stream.flatMap, comme on peut le deviner par son nom, est la combinaison d'un mapet d'une flatopération. Cela signifie que vous appliquez d'abord une fonction à vos éléments, puis l'aplatissez. Stream.mapapplique uniquement une fonction au flux sans aplatir le flux.

Pour comprendre en quoi consiste l' aplatissement d' un flux, considérons une structure comme celle [ [1,2,3],[4,5,6],[7,8,9] ]qui a "deux niveaux". Aplatissant ce moyen de transformation dans une structure « un niveau »: [ 1,2,3,4,5,6,7,8,9 ].

Dici
la source
5
simple and sweet
bluelurker
3
Haha, pour être honnête, je suis toujours surpris de voir combien de trafic cette question reçoit. Une autre observation amusante est que cela fait presque 5 ans que j'ai écrit cette réponse et qu'il y a eu un modèle assez constant de vote ascendant où la réponse acceptée obtient environ deux votes positifs pour chacun de mes réponses. C'est étonnamment cohérent.
Dici
1
comment est-ce que ce n'est pas la réponse acceptée, merci d'être
allé
233

Je voudrais donner 2 exemples pour avoir un point de vue plus pratique:
Premier exemple d'utilisation de la carte:

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

Rien de spécial dans le premier exemple, un Functionest appliqué pour renvoyer le Stringen majuscule.

Deuxième exemple utilisant 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);
}

Dans le deuxième exemple, un flux de liste est transmis. Ce n'est PAS un flux d'entiers!
Si une fonction de transformation doit être utilisée (via une carte), le flux doit d'abord être aplati en quelque chose d'autre (un flux de nombres entiers).
Si flatMap est supprimé, l'erreur suivante est renvoyée: L'opérateur + n'est pas défini pour la liste des types d'arguments, int.
Il n'est PAS possible d'appliquer + 1 sur une liste de nombres entiers!

Rudy Vissers
la source
@PrashanthDebbadwar Je pense que vous vous retrouveriez avec un flux de Stream<Integer>plutôt qu'un flux de Integer.
payne
166

Veuillez parcourir l'intégralité du message pour avoir une idée claire,

map vs flatMap:

Pour renvoyer une longueur de chaque mot d'une liste, nous ferions quelque chose comme ci-dessous.

Version courte donnée ci-dessous

Lorsque nous collectons deux listes, données ci-dessous

Sans carte plate => [1,2], [1,1] => [[1,2], [1,1]] Ici deux listes sont placées à l'intérieur d'une liste, donc la sortie sera une liste contenant des listes

Avec plan plat => [1,2], [1,1] => [1,2,1,1] Ici, deux listes sont aplaties et seules les valeurs sont placées dans la liste, donc la sortie sera une liste contenant uniquement des éléments

Fondamentalement, il fusionne tous les objets en un

## La version détaillée a été donnée ci-dessous: -

Par exemple: -
Considérons une liste ["STACK", "OOOVVVER"] et nous essayons de renvoyer une liste comme ["STACKOVER"] (en ne renvoyant que des lettres uniques de cette liste) Initialement, nous ferions quelque chose comme ci-dessous pour renvoyer un liste ["STACKOVER"] dans ["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());
  }
}

Ici, le problème est que Lambda passé à la méthode map renvoie un tableau de chaînes pour chaque mot, donc le flux renvoyé par la méthode map est en fait de type Stream, mais ce dont nous avons besoin est Stream pour représenter un flux de caractères, l'image ci-dessous illustre le problème.

Figure A:

entrez la description de l'image ici

Vous pourriez penser que, nous pouvons résoudre ce problème en utilisant une carte plate,
OK, voyons comment résoudre ce problème en utilisant map et Arrays.stream Tout d'abord, vous aurez besoin d'un flux de caractères au lieu d'un flux de tableaux. Il existe une méthode appelée Arrays.stream () qui prendrait un tableau et produirait un flux, par exemple:

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

Ce qui précède ne fonctionne toujours pas, car nous nous retrouvons maintenant avec une liste de flux (plus précisément, Stream>). À la place, nous devons d'abord convertir chaque mot en un tableau de lettres individuelles, puis transformer chaque tableau en un flux distinct

En utilisant flatMap, nous devrions pouvoir résoudre ce problème comme ci-dessous:

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 effectuerait le mappage de chaque tableau non pas avec un flux mais avec le contenu de ce flux. Tous les flux individuels qui seraient générés lors de l'utilisation de map (Arrays :: stream) sont fusionnés en un seul flux. La figure B illustre l'effet de l'utilisation de la méthode flatMap. Comparez-le avec la carte de la figure A. Figure B entrez la description de l'image ici

La méthode flatMap vous permet de remplacer chaque valeur d'un flux par un autre flux, puis joint tous les flux générés en un seul flux.

TechDog
la source
2
Belle explication schématique.
Hitesh
108

Réponse en une ligne: flatMapaide à aplatir un Collection<Collection<T>>en unCollection<T> . De la même manière, il s'aplatira également Optional<Optional<T>>en Optional<T>.

entrez la description de l'image ici

Comme vous pouvez le voir, avec map()seulement:

  • Le type intermédiaire est Stream<List<Item>>
  • Le type de retour est List<List<Item>>

et avec flatMap():

  • Le type intermédiaire est Stream<Item>
  • Le type de retour est List<Item>

Voici le résultat du test du code utilisé juste en dessous:

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

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

Code utilisé :

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);
  }
}
nxhoaf
la source
8
Exemple très net .., ne prendrait pas plus de quelques secondes pour comprendre le concept avec votre exemple ...
TechDog
2
belle explication. apprécie vraiment pour une explication simple et meilleure
Sachin Rane
42

La fonction à laquelle vous passez stream.mapdoit renvoyer un objet. Cela signifie que chaque objet du flux d'entrée génère exactement un objet dans le flux de sortie.

La fonction à laquelle vous passez stream.flatMaprenvoie un flux pour chaque objet. Cela signifie que la fonction peut renvoyer n'importe quel nombre d'objets pour chaque objet d'entrée (y compris aucun). Les flux résultants sont ensuite concaténés en un flux de sortie.

Philipp
la source
Pourquoi voudriez-vous "renvoyer un nombre quelconque d'objets pour chaque objet d'entrée (y compris aucun)"?
Derek Mahar
4
@DerekMahar Il y aurait de nombreux cas d'utilisation pour cela. Par exemple, supposons que vous ayez un flux de Departments dans votre organisation. Chaque département a entre 0 et n Employees. Vous avez besoin d'un flux de tous les employés. Donc que fais-tu? Vous écrivez une méthode flatMap qui prend un service et renvoie un flux de ses employés.
Philipp
Philipp, votre exemple illustre-t-il la principale raison d'utiliser flatMap? Je soupçonne que cela peut être accessoire et n'illustre pas le cas d'utilisation clé ou la raison de son flatMapexistence. (Suite ci-dessous ...)
Derek Mahar
Après avoir lu dzone.com/articles/understanding-flatmap , je pense que la principale motivation derrière flatMapest de tenir compte des erreurs qui seraient présentes lors de l'utilisation map. Comment gérez-vous les cas où un ou plusieurs éléments de l'ensemble d'origine ne peuvent pas être mappés à un élément de sortie? En introduisant un ensemble intermédiaire (disons un Optionalou Stream) pour chaque objet d'entrée, flatMapvous permet d'exclure les objets d'entrée "non valides" (ou les "mauvaises pommes" dans l'esprit de stackoverflow.com/a/52248643/107158 ) de la ensemble final.
Derek Mahar
1
@DerekMahar Oui, les évaluations où chaque objet d'entrée peut ou non renvoyer un objet de sortie est un autre bon cas d'utilisation pour la carte plate.
Philipp
29

pour une carte, nous avons une liste d'éléments et une (fonction, action) f donc:

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

et pour la carte plate nous avons une liste de liste d'éléments et nous avons une (fonction, action) f et nous voulons que le résultat soit aplati:

[[a,b],[c,d,e]] f(x) =>[f(a),f(b),f(c),f(d),f(e)]
Bachiri Taoufiq Abderrahman
la source
25

J'ai le sentiment que la plupart des réponses ici compliquent le problème simple. Si vous comprenez déjà commentmap travaux devraient être assez faciles à saisir.

Il y a des cas où nous pouvons nous retrouver avec des structures imbriquées indésirables lors de l'utilisation map(), la flatMap()méthode est conçue pour surmonter cela en évitant le wrapping.


Exemples:

1

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

Nous pouvons éviter d'avoir des listes imbriquées en utilisant 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));

où:

private Optional<String> findById(Integer id)
Grzegorz Piwowarek
la source
désolé mais le 2e extrait du point 1 ne se compile pas à la place de List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(i -> i) .collect(Collectors.toList());. Il devrait êtreStream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(List::stream) .collect(Collectors.toList());
Arthur
@arthur Je pense que j'ai utilisé le flux et la liste de Vavr ici - mais je conviens que cela pourrait être un peu déroutant - je changerai cela en Java standard
Grzegorz Piwowarek
@GrzegorzPiwowarek que diriez-vous de cette explication simple ?
Eugene
22

L'article d'Oracle sur Facultatif met en évidence cette différence entre la carte et la carte plate:

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

Malheureusement, ce code ne se compile pas. Pourquoi? L'ordinateur variable est de type Optional<Computer>, il est donc parfaitement correct d'appeler la méthode map. Cependant, getSoundcard () renvoie un objet de type Facultatif. Cela signifie que le résultat de l'opération de mappage est un objet de typeOptional<Optional<Soundcard>> . Par conséquent, l'appel à getUSB () n'est pas valide car le plus à l'extérieur facultatif contient comme valeur un autre facultatif, qui bien sûr ne prend pas en charge la méthode getUSB ().

Avec les flux, la méthode flatMap prend une fonction en argument, qui renvoie un autre flux. Cette fonction est appliquée à chaque élément d'un flux, ce qui entraînerait un flux de flux. Cependant, flatMap a pour effet de remplacer chaque flux généré par le contenu de ce flux. En d'autres termes, tous les flux séparés générés par la fonction sont fusionnés ou «aplatis» en un seul flux. Ce que nous voulons ici est quelque chose de similaire, mais nous voulons "aplatir" une option à deux niveaux en un seul .

Facultatif prend également en charge une méthode flatMap. Son objectif est d'appliquer la fonction de transformation sur la valeur d'un Facultatif (comme le fait l'opération de mappage), puis d' aplatir le Facultatif à deux niveaux résultant en un seul .

Donc, pour rendre notre code correct, nous devons le réécrire comme suit en utilisant flatMap:

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

Le premier flatMap garantit que an Optional<Soundcard>est retourné au lieu d'un Optional<Optional<Soundcard>>, et le second flatMap atteint le même objectif que de retourner un Optional<USB>. Notez que le troisième appel doit simplement être un map () car getVersion () renvoie une chaîne plutôt qu'un objet facultatif.

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

Noyau rouillé
la source
1
la question portait sur Stream.map et Stream.flatMap et non sur Optional.map anfd Optional.flatMap
djames
4
Mais cela m'a beaucoup aidé à comprendre mes problèmes avec les cartes optionnelles et flatmap, merci beaucoup!
Loïc
2
@djames, c'est une réponse parfaitement valable, lisez-la à partir du paragraphe "Avec les streams, la méthode flatMap prend une fonction comme argument ..." :)
skwisgaar
Je pense que c'est un ajout très utile à certaines des autres réponses ici.
Mz A
La version flatMap () lève également nullpointerexception si soundCard est null. Alors, où est l'avantage promis d'Optional?
Ekaterina
16

Je ne suis pas très sûr d'être censé répondre à cela, mais chaque fois que je rencontre quelqu'un qui ne comprend pas cela, j'utilise le même exemple.

Imaginez que vous ayez une pomme. A maptransforme cette pomme apple-juicepar exemple en une cartographie biunivoque.

Prenez cette même pomme et n'en retirez que les graines, c'est ce qui flatMapfait, ou une à plusieurs , une pomme en entrée, de nombreuses graines en sortie.

Eugène
la source
4
C'est un exemple intéressant :)
cassiomoline
Pour le flatMapcas, collectez-vous d'abord les graines de chaque pomme dans des sacs séparés, un sac par pomme, avant de verser tous les sacs dans un seul sac?
Derek Mahar
@DerekMahar c'était un pauvre dans un seul sac avant java-10, ce qui flatmapn'était pas vraiment paresseux, mais depuis java-10 c'est paresseux
Eugene
@Eugene s'il vous plaît expliquer un concept un peu plus paresseux que vous essayez d'expliquer pas clair pour moi. J'ai compris ce que derkerMahar a expliqué en commentaire est-ce que ce qui se passe avant java10?
JAVA
@JAVA il suffit de chercher flatMap + lazy, je parie qu'il y aura des réponses.
Eugene
16

map () et flatMap ()

  1. map()

Prend simplement une fonction un paramètre lambda où T est l'élément et R l'élément de retour construit à l'aide de T. À la fin, nous aurons un flux avec des objets de type R. Un exemple simple peut être:

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

Il prend simplement les éléments 1 à 5 de Type Integer, utilise chaque élément pour construire un nouvel élément à partir de type Stringavec valeur "prefix_"+integer_valueet l'imprime.

  1. flatMap()

Il est utile de savoir que flatMap () prend une fonction F<T, R>

  • T est un type à partir duquel un Stream peut être construit à partir de / avec . Ce peut être une List (T.stream ()), un tableau (Arrays.stream (someArray)), etc. tout ce dont un Stream peut être avec / ou forme. dans l'exemple ci-dessous, chaque dev a plusieurs langues, donc dev. Les langues sont une liste et utiliseront un paramètre lambda.

  • R est le flux résultant qui sera construit à l'aide de T. Sachant que nous avons de nombreuses instances de T, nous aurons naturellement de nombreux flux de R. Tous ces flux de type R seront désormais combinés en un seul flux `` plat '' de type R .

Exemple

Les exemples de Bachiri Taoufiq voir sa réponse ici sont simples et faciles à comprendre. Pour plus de clarté, disons simplement que nous avons une équipe de développeurs:

dev_team = {dev_1,dev_2,dev_3}

, chaque développeur connaissant plusieurs langues:

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

Appliquer Stream.map () sur dev_team pour obtenir les langues de chaque développeur:

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

vous donnera cette structure:

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

qui est essentiellement un List<List<Languages>> /Object[Languages[]]. Pas très joli, ni Java8 !!

avec Stream.flatMap()vous pouvez `` aplatir '' les choses en prenant la structure ci-dessus
et en la transformant {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}, qui peut essentiellement être utilisée comme List<Languages>/Language[]/etc...

donc à la fin, votre code aurait plus de sens comme ceci:

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

ou simplement:

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

Quand utiliser map () et flatMap () :

  • À utiliser map()lorsque chaque élément de type T de votre flux est censé être mappé / transformé en un seul élément de type R. Le résultat est un mappage de type (1 élément de départ -> 1 élément de fin) et un nouveau flux d'éléments de type R est retourné.

  • À utiliser flatMap()lorsque chaque élément de type T de votre flux est censé être mappé / transformé en collections d'éléments de type R. Le résultat est un mappage de type (1 élément de début -> n éléments de fin) . Ces collections sont ensuite fusionnées (ou aplaties ) dans un nouveau flux d'éléments de type R. Ceci est utile par exemple pour représenter des boucles imbriquées .

Pré 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()));
Arthur
la source
11

Carte: - Cette méthode prend une fonction comme argument et retourne un nouveau flux composé des résultats générés en appliquant la fonction passée à tous les éléments du flux.

Imaginons, j'ai une liste de valeurs entières (1,2,3,4,5) et une interface de fonction dont la logique est le carré de l'entier passé. (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);

production:-

[1, 4, 9, 16, 25]

Comme vous pouvez le voir, une sortie est un nouveau flux dont les valeurs sont des carrés de valeurs du flux d'entrée.

[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: - Cette méthode prend une fonction comme argument, cette fonction accepte un paramètre T comme argument d'entrée et retourne un flux de paramètre R comme valeur de retour. Lorsque cette fonction est appliquée à chaque élément de ce flux, elle produit un flux de nouvelles valeurs. Tous les éléments de ces nouveaux flux générés par chaque élément sont ensuite copiés dans un nouveau flux, qui sera une valeur de retour de cette méthode.

Imaginons, j'ai une liste d'objets étudiants, où chaque étudiant peut opter pour plusieurs sujets.

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

production:-

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

Comme vous pouvez le voir, une sortie est un nouveau flux dont les valeurs sont une collection de tous les éléments des flux renvoyés par chaque élément du flux d'entrée.

[S1, S2, S3] -> [{"histoire", "math", "géographie"}, {"économie", "biologie"}, {"science", "math"}] -> prendre des sujets uniques - > [économie, biologie, géographie, science, histoire, mathématiques]

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

lalitbhagtani
la source
pourrait faire une différence si vous fournissez du code au lieu de simplement prouver le lien doc
Charles-Antoine Fournel
11

.map est pour le mappage A -> B

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

il convertit n'importe quel élément Aen n'importe quel élément B. Javadoc


.flatMap est pour 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]

il --1 convertit n'importe quel élément Aen Stream< B>, puis --2 concatène tous les flux en un seul flux (plat). Javadoc


Remarque 1: Bien que ce dernier exemple se transforme en un flux de primitives (IntStream) au lieu d'un flux d'objets (Stream), il illustre toujours l'idée du .flatMap.

Remarque 2: Malgré le nom, la méthode String.chars () renvoie des entiers. Ainsi, la collection réelle sera:, [100, 111, 103, 99, 97, 116]100est le code de 'd', 111est le code de 'o'etc. Encore une fois, à des fins d'illustration, il est présenté comme [d, o, g, c, a, t].

épox
la source
3
Meilleure réponse. Droit au but avec des exemples
GabrielBB
1

Réponse simple.

L' mapopération peut produire un Streamde Stream.EXStream<Stream<Integer>>

flatMapl'opération ne produira que Streamquelque chose. EXStream<Integer>

Melad Basilius
la source
0

Une bonne analogie peut également être avec C # si vous êtes familier avec. Fondamentalement C # Selectsimilaire à java mapet C # SelectManyjava flatMap. Il en va de même pour Kotlin pour les collections.

Arsenius
la source
0

C'est très déroutant pour les débutants. La différence de base est mapémet un élément pour chaque entrée de la liste et flatMapest fondamentalement une opération map+ flatten. Pour être plus clair, utilisez flatMap lorsque vous avez besoin de plusieurs valeurs, par exemple lorsque vous vous attendez à ce qu'une boucle retourne des tableaux, flatMap sera vraiment utile dans ce cas.

J'ai écrit un blog à ce sujet, vous pouvez le vérifier ici .

Niraj Chauhan
la source
0

Opérations de streaming flatMapet mapacceptation d'une fonction en entrée.

flatMapattend de la fonction qu'elle renvoie un nouveau flux pour chaque élément du flux et renvoie un flux qui combine tous les éléments des flux renvoyés par la fonction pour chaque élément. En d'autres termes, avec flatMap, pour chaque élément de la source, plusieurs éléments seront créés par la fonction. http://www.zoftino.com/java-stream-examples#flatmap-operation

mapattend que la fonction retourne une valeur transformée et retourne un nouveau flux contenant les éléments transformés. En d'autres termes, avec map, pour chaque élément de la source, un élément transformé sera créé par la fonction. http://www.zoftino.com/java-stream-examples#map-operation

Arnav Rao
la source
0

flatMap()tire également parti de l'évaluation paresseuse partielle des flux. Il lira le premier flux et uniquement lorsque cela sera nécessaire, ira au flux suivant. Le comportement est expliqué en détail ici: FlatMap est-il garanti d'être paresseux?

SK
la source
0

Si vous pensez map() comme une itération ( forboucle à un niveau ), flatmap()est une itération à deux niveaux (comme une forboucle imbriquée ). (Entrez chaque élément itéré foo, et recommencez foo.getBarList()et barListrecommencez)


map(): prendre un flux, faire quelque chose pour chaque élément, collecter le résultat unique de chaque processus, sortir un autre flux. La définition de "faire quelque chose" est implicite. Si le traitement d'un élément entraîne null, nullest utilisé pour composer le flux final. Ainsi, le nombre d'éléments dans le flux résultant sera égal au nombre de flux d'entrée.

flatmap(): prendre un flux de éléments / flux et une fonction (définition explicite), appliquer la fonction à chaque élément de chaque flux, et collecter tous les flux intermédiaires résultants pour être un flux plus important ("flattening"). Si le traitement d'un élément entraîne null, un flux vide est fourni à l'étape finale de l'aplatissement. Le nombre d'éléments dans le flux résultant est le total de tous les éléments participants dans toutes les entrées, si l'entrée est constituée de plusieurs flux.

WesternGun
la source