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


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


411
2017-10-31 22:53


origine


Réponses:


Tous les deux map et flatMap peut être appliqué à un Stream<T> et ils retournent tous deux un Stream<R>. La différence est que le map l'opération produit une valeur de sortie pour chaque valeur d'entrée, tandis que le flatMap L'opération produit un nombre arbitraire (zéro ou plus) pour chaque valeur d'entrée.

Ceci est reflété dans les arguments de chaque opération.

le map l'opération prend un Function, qui est appelée pour chaque valeur dans le flux d'entrée et produit une valeur de résultat, qui est envoyée au flux de sortie.

le flatMap l'opération prend une fonction qui veut conceptuellement consommer une valeur et produire un nombre arbitraire de valeurs. Cependant, en Java, il est fastidieux pour une méthode de renvoyer un nombre arbitraire de valeurs, puisque les méthodes ne peuvent renvoyer que zéro ou une seule valeur. On pourrait imaginer une API où la fonction de mappeur pour flatMap prend une valeur et retourne un tableau ou un List des valeurs, qui sont ensuite envoyées à la sortie. Étant donné qu'il s'agit de la bibliothèque de flux, une manière particulièrement appropriée de représenter un nombre arbitraire de valeurs de retour est que la fonction de mappeur retourne elle-même un flux! Les valeurs du flux renvoyé par le mappeur sont drainées du flux et sont transmises au flux de sortie. Les "amas" 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".

Une utilisation typique est la fonction de mappeur de flatMap revenir Stream.empty() s'il veut envoyer des valeurs nulles, ou quelque chose comme Stream.of(a, b, c) s'il veut retourner plusieurs valeurs. Mais bien sûr, n'importe quel flux peut être retourné.


512
2017-10-31 23:11



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

Pour comprendre quoi aplanissement un flux consiste en, considérer une structure comme [ [1,2,3],[4,5,6],[7,8,9] ] qui a "deux niveaux". Aplatir cela signifie le transformer en une structure "à un niveau": [ 1,2,3,4,5,6,7,8,9 ].


262
2017-10-31 22:56



Je voudrais donner 2 exemples pour obtenir un plus point de vue pratique:
1er exemple faisant usage 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 Function est appliqué pour retourner le String en majuscule.

Deuxième exemple d'utilisation de 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 second exemple, un flux de liste est passé. Ce n'est pas un flux entier!
Si une fonction de transformation doit être utilisée (à travers une carte), alors le Stream doit d'abord être aplati pour autre chose (un Stream of Integer).
Si flatMap est supprimé, l'erreur suivante est renvoyée: L'opérateur + n'est pas défini pour le (s) type (s) d'argument List, int.
Il n'est pas possible d'appliquer + 1 sur une liste de nombres entiers!


164
2017-12-17 10:19



S'il vous plaît passer par le poste pleinement pour obtenir une idée claire, carte vs flatMap: Pour retourner une longueur de chaque mot d'une liste, nous ferions quelque chose comme ci-dessous ..

Par exemple:- 
Considérer une liste ["STACK", "OOOVVVER"] et nous essayons de retourner une liste comme ["STACKOVER"](ne renvoyer que des lettres uniques de cette liste) Au départ, nous ferions quelque chose comme ci-dessous pour retourner une liste ["STACKOVER"]  de ["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 String pour chaque mot. Donc, le flux renvoyé par la méthode map est en fait de type Stream, mais ce dont nous avons besoin, c'est que Stream représente un flux de caractères. problème.

Figure A:

enter image description here

Vous pourriez penser que, Nous pouvons résoudre ce problème en utilisant flatmap,
 OK, laissez-nous voir comment résoudre cela en utilisant carte 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: enter image description here

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

En utilisant flatmap, nous devrions pouvoir résoudre ce problème comme suit:

enter image description here

flatMap effectue le mappage de chaque tableau non avec le flux mais avec le contenu de ce flux. Tous les flux individuels 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 ce que fait la carte dans la figure A. Figure B enter image description here

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


84
2018-05-09 16:18



La fonction que vous passez à stream.map doit retourner 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 que vous passez à stream.flatMap retourne 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.


35
2017-10-31 22:58



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

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

et pour la carte plate nous avons une liste d'éléments listés 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)]

17
2017-10-08 03:19



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

Il y a des cas où nous pouvons finir avec des structures imbriquées indésirables en utilisant map(), la flatMap() méthode est conçue pour surmonter cela en évitant d'emballage.


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)

17
2017-07-25 17:41



L'article d'Oracle sur Optional met en évidence cette différence entre map et flatmap:

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

Malheureusement, ce code ne compile pas. Pourquoi? L'ordinateur variable   est de type Optional<Computer>, il est donc parfaitement correct d'appeler le   méthode cartographique. Cependant, getSoundcard () renvoie un objet de type   Optionnel. Cela signifie que le résultat de l’opération de la carte est un   objet de type Optional<Optional<Soundcard>>. En conséquence, l'appel à   getUSB () est invalide car l'option externe la plus externe contient comme   value un autre optionnel, qui bien sûr ne supporte pas le getUSB ()   méthode.

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   sont générés par la fonction se fusionner ou "aplati" en un   flux unique. Ce que nous voulons ici est quelque chose de similaire, mais nous voulons   "aplatir" un optionnel à deux niveaux en un.

Facultatif prend également en charge une méthode flatMap. Son but est d'appliquer   la fonction de transformation sur la valeur d'un optionnel (tout comme la carte   opération fait) et ensuite 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 qu'un Optional<Soundcard> est retourné   au lieu d'un Optional<Optional<Soundcard>>, et la deuxième carte plate   atteint le même objectif de retourner un Optional<USB>. Notez que le   le troisième appel doit simplement être un map () car getVersion () renvoie un   Chaîne plutôt qu'un objet facultatif.

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


16
2018-02-24 19:54



Carte:- Cette méthode prend une fonction comme argument et renvoie un nouveau flux composé des résultats générés en appliquant la fonction transmise à 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 un carré du nombre 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);

sortie:-

[1, 4, 9, 16, 25]

Comme vous pouvez le voir, une sortie est un nouveau flux dont les valeurs sont des valeurs carrées 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 renvoie un flux du paramètre R comme valeur de retour. Lorsque cette fonction est appliquée à chaque élément de ce flux, elle génère 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.

Laissons l'image, j'ai une liste d'objets étudiants, où chaque élève 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);

sortie:-

[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"}] -> prenez des sujets uniques -> [économie, biologie, géographie, science, histoire, mathématiques]

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


9
2018-02-10 10:44



Aussi bonne analogie peut être avec C # si vous connaissez. Fondamentalement C # Select similaire à java map et C# SelectMany Java flatMap. La même chose s'applique à Kotlin pour les collections.


1
2018-06-14 04:12