¿Cuál es la diferencia entre los métodos map () y flatMap () en Java 8?

707

En Java 8, ¿cuál es la diferencia entre Stream.map()y los Stream.flatMap()métodos?

cassiomolin
fuente
55
El tipo de firma cuenta un poco toda la historia. map :: Stream T -> (T -> R) -> Stream R, flatMap :: Stream T -> (T -> Stream R) -> Stream R.
Chris Martin
98
fwiw, esas firmas tipo ni siquiera se parecen a Java. (Lo sé, lo sé, pero decir que cuenta "toda la historia" wrt map / flatMap supone un gran conocimiento sobre el nuevo y mejorado "Java ++")
michael
16
@michael Ese tipo de firma se parece a Haskell, no a Java. Pero no es claro si la firma real de Java es más legible: <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper).
Stuart Marks
8
Ja, sí, me refería a la "Java real". Al igual que C ++, Java moderno es casi irreconocible para cualquiera que comenzó a usarlo en los años 90 (como lo hice, ambos idiomas). Simplemente respondiendo al comentario, las firmas de métodos apenas cuentan una "historia completa", al menos no más, no sin una exposición adicional (o en el caso de los comentaristas, traducción).
michael
2
Es decir, mapel mapeador lambda de a regresa R, flatMapel mapeador lambda de a regresa un Streamde R( Stream<R>). Las secuencias devueltas por el flatMapmapeador de 's se concatenan efectivamente. De lo contrario, ambos mapy flatMapvolver Stream<R>; la diferencia es lo que devuelven los lambdas asignador, Rvs Stream<R>.
derekm

Respuestas:

816

Ambos mapy flatMapse pueden aplicar a a Stream<T>y ambos devuelven a Stream<R>. La diferencia es que la mapoperación produce un valor de salida para cada valor de entrada, mientras que la flatMapoperación produce un número arbitrario (cero o más) valores para cada valor de entrada.

Esto se refleja en los argumentos de cada operación.

La mapoperación toma un Function, que se llama para cada valor en la secuencia de entrada y produce un valor de resultado, que se envía a la secuencia de salida.

La flatMapoperación toma una función que conceptualmente quiere consumir un valor y producir un número arbitrario de valores. Sin embargo, en Java, es engorroso que un método devuelva un número arbitrario de valores, ya que los métodos solo pueden devolver un valor cero o uno. Uno podría imaginar una API donde la función del mapeador flatMaptoma un valor y devuelve una matriz o unListde valores, que luego se envían a la salida. Dado que esta es la biblioteca de secuencias, una forma particularmente adecuada de representar un número arbitrario de valores de retorno es que la función del mapeador en sí misma devuelva una secuencia. Los valores de la secuencia devueltos por el asignador se drenan de la secuencia y se pasan a la secuencia de salida. Los "grupos" de valores devueltos por cada llamada a la función del mapeador no se distinguen en absoluto en el flujo de salida, por lo tanto, se dice que la salida se ha "aplanado".

El uso típico es que la función de mapeador de flatMapdevolver Stream.empty()si quiere enviar valores cero, o algo así como Stream.of(a, b, c)si quiere devolver varios valores. Pero, por supuesto, cualquier flujo puede ser devuelto.

Stuart Marks
fuente
26
Me parece que la flatMapoperación es exactamente lo contrario de plana. Una vez más, déjelo en manos de los informáticos para convertir un término en su cabeza. Al igual que una función que es "transparente", lo que significa que no puede ver nada de lo que hace, solo los resultados, mientras que coloquialmente dice que desea que un proceso sea transparente significa que quiere que se vean todas sus partes.
Coladict
45
@coladict Intente verlo desde una perspectiva diferente: no es un caso transparente en el que puede ver el funcionamiento interno, sino que toda la función en sí misma es transparente, es decir, invisible, mientras realiza su trabajo y le permite ver lo que usted hace ' Re trabajando con. En este caso, "plano" se refiere al opuesto de "anidado", el mapa plano elimina un nivel de anidamiento al aplanar.
Zefiro
77
@coladict Lo "transparente" me ha estado comiendo la cabeza durante años. Me alegra saber que al menos otra persona siente lo mismo.
Ashok Bijoy Debnath
99
El aplanamiento viene de convertir la estructura de 2 niveles en una estructura de un solo nivel, vea la respuesta de Dici para un ejemplo stackoverflow.com/a/26684582/6012102
andrzej.szmukala
26
Esta es la mejor explicación de flatMap . Esto es lo que hace que se haga clic: los valores de la secuencia devueltos por el asignador se drenan de la secuencia y se pasan a la secuencia de salida. Los "grupos" de valores devueltos por cada llamada a la función del mapeador no se distinguen en absoluto en el flujo de salida, por lo tanto, se dice que la salida ha sido "aplanada" . ¡Gracias!
neevek
464

Stream.flatMap, como se puede adivinar por su nombre, es la combinación de ay mapuna flatoperación. Eso significa que primero aplica una función a sus elementos y luego la aplana. Stream.mapsolo aplica una función a la secuencia sin aplanarla.

Para comprender en qué consiste el aplanamiento de una secuencia, considere una estructura como la [ [1,2,3],[4,5,6],[7,8,9] ]que tiene "dos niveles". El aplanamiento de este medio transformándolo en una estructura de "un nivel": [ 1,2,3,4,5,6,7,8,9 ].

Dici
fuente
55
simple y dulce
bluelurker
3
Jaja, para ser justos, todavía me sorprende ver cuánto tráfico recibe esta pregunta. Otra observación divertida es que han pasado casi 5 años que escribí esta respuesta y ha habido un patrón bastante consistente de votación positiva donde la respuesta aceptada obtiene aproximadamente dos votos positivos por cada uno que recibe mi respuesta. Es sorprendentemente consistente.
Dici
1
¿Cómo es que esta no es la respuesta aceptada? Gracias por ir directo al grano y poner un ejemplo muy simple
1mike12
233

Me gustaría dar 2 ejemplos para obtener un punto de vista más práctico:
Primer ejemplo haciendo uso del mapa:

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

Nada especial en el primer ejemplo, Functionse aplica a para devolver la Stringmayúscula.

Segundo ejemplo haciendo uso 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);
}

En el segundo ejemplo, se pasa una secuencia de lista. ¡NO es un flujo de enteros!
Si se debe usar una Función de transformación (a través del mapa), primero se debe aplanar la Corriente a otra cosa (una Corriente de Entero).
Si se elimina flatMap, se devuelve el siguiente error: el operador + no está definido para la lista de tipos de argumento, int.
¡NO es posible aplicar + 1 en una Lista de enteros!

Rudy Vissers
fuente
@PrashanthDebbadwar Creo que terminarías con un Stream en Stream<Integer>lugar de un Stream de Integer.
Payne
166

Revisa la publicación por completo para tener una idea clara,

map vs flatMap:

Para devolver una longitud de cada palabra de una lista, haríamos algo como a continuación.

Versión corta dada a continuación

Cuando recopilamos dos listas, dadas a continuación

Sin mapa plano => [1,2], [1,1] => [[1,2], [1,1]] Aquí se colocan dos listas dentro de una lista, por lo que la salida será una lista que contiene listas

Con mapa plano => [1,2], [1,1] => [1,2,1,1] Aquí se aplanan dos listas y solo los valores se colocan en la lista, por lo que la salida será una lista que solo contiene elementos

Básicamente combina todos los objetos en uno

## La versión detallada se ha proporcionado a continuación: -

Por ejemplo: -
Considere una lista ["STACK", "OOOVVVER"] y estamos tratando de devolver una lista como ["STACKOVER"] (devolviendo solo letras únicas de esa lista) Inicialmente, haríamos algo como a continuación para devolver un lista ["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());
  }
}

Aquí el problema es que Lambda pasado al método de mapa devuelve una matriz de cadenas para cada palabra, por lo que la secuencia devuelta por el método de mapa es en realidad de tipo Stream, pero lo que necesitamos es Stream para representar una secuencia de caracteres, la imagen de abajo ilustra el problema.

Figura A

ingrese la descripción de la imagen aquí

Puede pensar que, podemos resolver este problema usando un mapa plano,
OK, veamos cómo resolver esto usando map y Arrays.stream En primer lugar, necesitará una secuencia de caracteres en lugar de una secuencia de matrices. Hay un método llamado Arrays.stream () que tomaría una matriz y produciría una secuencia, por ejemplo:

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

Lo anterior aún no funciona, porque ahora terminamos con una lista de secuencias (más precisamente, Stream>). En su lugar, primero debemos convertir cada palabra en una matriz de letras individuales y luego hacer que cada matriz se convierta en una secuencia separada

Al usar flatMap deberíamos poder solucionar este problema de la siguiente manera:

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 realizaría el mapeo de cada matriz no con la secuencia sino con el contenido de esa secuencia. Todas las secuencias individuales que se generarían al usar el mapa (Arrays :: stream) se fusionan en una sola secuencia. La Figura B ilustra el efecto del uso del método flatMap. Compárelo con lo que hace el mapa en la figura A. Figura B ingrese la descripción de la imagen aquí

El método flatMap le permite reemplazar cada valor de una secuencia con otra secuencia y luego une todas las secuencias generadas en una sola secuencia.

TechDog
fuente
2
Buena explicación esquemática.
Hitesh
108

Respuesta de una línea: flatMapayuda a aplanar a Collection<Collection<T>>en aCollection<T> . De la misma manera, también aplanará un Optional<Optional<T>>en Optional<T>.

ingrese la descripción de la imagen aquí

Como puedes ver, map()solo con :

  • El tipo intermedio es Stream<List<Item>>
  • El tipo de retorno es List<List<Item>>

y con flatMap():

  • El tipo intermedio es Stream<Item>
  • El tipo de retorno es List<Item>

Este es el resultado de la prueba del código utilizado a continuación:

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

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

Código usado :

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
fuente
8
Ejemplo muy nítido .., ¿no toma más de unos pocos segundos para entender el concepto con su ejemplo ...
TechDog
2
Buena explicación realmente aprecio por una explicación simple y mejor
Sachin Rane
42

La función a la que pasa stream.maptiene que devolver un objeto. Eso significa que cada objeto en la secuencia de entrada da como resultado exactamente un objeto en la secuencia de salida.

La función a la que pasa stream.flatMapdevuelve una secuencia para cada objeto. Eso significa que la función puede devolver cualquier número de objetos para cada objeto de entrada (incluido ninguno). Las secuencias resultantes se concatenan a una secuencia de salida.

Philipp
fuente
¿Por qué querría "devolver cualquier número de objetos para cada objeto de entrada (incluido ninguno)"?
Derek Mahar
44
@DerekMahar Habría muchos casos de uso para esto. Por ejemplo, supongamos que tiene una secuencia de Departments en su organización. Cada departamento tiene entre 0 y n Employees. Lo que necesita es un flujo de todos los empleados. Entonces, ¿Qué haces? Escribe un método flatMap que toma un departamento y devuelve una secuencia de sus empleados.
Philipp
Philipp, ¿tu ejemplo ilustra la razón principal para usar flatMap? Sospecho que puede ser incidental y no ilustra el caso de uso clave o la razón por la cual flatMapexiste. (Continúa abajo ...)
Derek Mahar
Después de leer dzone.com/articles/understanding-flatmap , creo que la principal motivación detrás flatMapes acomodar los errores que estarían presentes al usarlos map. ¿Cómo maneja los casos en que uno o más elementos del conjunto original no pueden asignarse a un elemento de salida? Al introducir un conjunto intermedio (digamos un Optionalo Stream) para cada objeto de entrada, le flatMappermite excluir los objetos de entrada "no válidos" (o las llamadas "manzanas podridas" en el espíritu de stackoverflow.com/a/52248643/107158 ) del conjunto final
Derek Mahar
1
@DerekMahar Sí, las situaciones en las que cada objeto de entrada puede o no devolver un objeto de salida es otro buen caso de uso para el mapa plano.
Philipp
29

para un mapa tenemos una lista de elementos y a (función, acción) f entonces:

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

y para el mapa plano tenemos una lista de elementos y tenemos una (función, acción) f y queremos que el resultado sea plano:

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

Tengo la sensación de que la mayoría de las respuestas aquí complican demasiado el simple problema. Si ya comprende cómo mapfunciona, eso debería ser bastante fácil de entender.

Hay casos en los que podemos terminar con estructuras anidadas no deseadas cuando se usa map(), el flatMap()método está diseñado para superar esto evitando el ajuste.


Ejemplos:

1

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

Podemos evitar tener listas anidadas usando 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));

dónde:

private Optional<String> findById(Integer id)
Grzegorz Piwowarek
fuente
lo siento, pero el segundo fragmento del punto 1 no se está compilando en lugar de List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(i -> i) .collect(Collectors.toList());. Debe serStream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(List::stream) .collect(Collectors.toList());
Arthur
@arthur Creo que usé el Stream y la Lista de Vavr aquí, pero estoy de acuerdo en que puede ser un poco confuso.
Cambiaré
@GrzegorzPiwowarek ¿qué tal esta simple explicación ?
Eugene
22

El artículo de Oracle sobre Opcional destaca esta diferencia entre mapa y mapa plano:

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

Desafortunadamente, este código no se compila. ¿Por qué? La computadora variable es de tipo Optional<Computer>, por lo que es perfectamente correcto llamar al método de mapa. Sin embargo, getSoundcard () devuelve un objeto de tipo Opcional. Esto significa que el resultado de la operación del mapa es un objeto de tipo Optional<Optional<Soundcard>>. Como resultado, la llamada a getUSB () no es válida porque el Opcional más externo contiene como valor otro Opcional, que por supuesto no admite el método getUSB ().

Con las secuencias, el método flatMap toma una función como argumento, que devuelve otra secuencia. Esta función se aplica a cada elemento de una secuencia, lo que daría como resultado una secuencia de secuencias. Sin embargo, flatMap tiene el efecto de reemplazar cada flujo generado por el contenido de ese flujo. En otras palabras, todos los flujos separados que son generados por la función se amalgaman o "aplanan" en un solo flujo. Lo que queremos aquí es algo similar, pero queremos "aplanar" un Opcional de dos niveles en uno .

Opcional también admite un método flatMap. Su propósito es aplicar la función de transformación en el valor de un Opcional (al igual que la operación de mapa) y luego aplanar el Opcional de dos niveles resultante en uno solo .

Entonces, para corregir nuestro código, necesitamos reescribirlo de la siguiente manera usando flatMap:

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

El primer flatMap asegura que Optional<Soundcard>se devuelva un an en lugar de un an Optional<Optional<Soundcard>>, y el segundo flatMap logra el mismo propósito para devolver un an Optional<USB>. Tenga en cuenta que la tercera llamada solo necesita ser un mapa () porque getVersion () devuelve una Cadena en lugar de un objeto Opcional.

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

Núcleo oxidado
fuente
1
la pregunta era sobre Stream.map y Stream.flatMap y no sobre Optional.map y Optional.flatMap
djames
44
Pero me ayudó mucho entender mis problemas con el mapa plano y opcional, ¡muchas gracias!
Loïc
2
@djames, es una respuesta perfectamente válida, leyó a partir del párrafo "Con corrientes, el método flatMap toma una función como argumento ..." :)
Skwisgaar
Creo que esta es una adición muy útil a algunas de las otras respuestas aquí.
Mz A
La versión flatMap () también arroja nullpointerexception si soundCard es nulo. Entonces, ¿dónde está el beneficio prometido de Opcional?
Ekaterina
16

No estoy muy seguro de que deba responder esto, pero cada vez que me enfrento a alguien que no entiende esto, uso el mismo ejemplo.

Imagina que tienes una manzana. A mapestá transformando esa manzana, apple-juicepor ejemplo , en un mapeo uno a uno .

Tome esa misma manzana y obtenga solo las semillas, eso es lo que flatMaphace, o una a muchas , una manzana como entrada, muchas semillas como salida.

Eugene
fuente
44
Ese es un ejemplo interesante :)
cassiomolin
Para el flatMapcaso, ¿primero recolecta las semillas de cada manzana en bolsas separadas, una bolsa por manzana, antes de verter todas las bolsas en una sola bolsa?
Derek Mahar
@DerekMahar solía ser pobre en una sola bolsa antes de java-10, lo que significa que flatmapno era realmente vago, pero desde java-10 es vago
Eugene
@Eugene, por favor, explique un concepto un poco más vago que está tratando de explicar, no me queda claro. Comprendí lo que derkerMahar explicó en el comentario, ¿es eso lo que sucedió antes de java10?
JAVA
@JAVA solo busca flatMap + lazy, apuesto a que habrá algunas respuestas.
Eugene
16

map () y flatMap ()

  1. map()

Simplemente toma una función un parámetro lambda donde T es un elemento y R el elemento de retorno construido utilizando T. Al final tendremos una secuencia con objetos de tipo R. Un ejemplo simple puede ser:

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

Simplemente toma los elementos 1 a 5 de Tipo Integer, usa cada elemento para construir un nuevo elemento a partir de tipo Stringcon valor "prefix_"+integer_valuey lo imprime.

  1. flatMap()

Es útil saber que flatMap () toma una función F<T, R>donde

  • T es un tipo a partir del cual se puede construir un Stream desde / con . Puede ser una Lista (T.stream ()), una matriz (Arrays.stream (someArray)), etc. cualquier cosa desde la cual un Stream puede ser con / o forma. en el siguiente ejemplo, cada desarrollador tiene muchos idiomas, por lo que el desarrollador. Idiomas es una lista y utilizará un parámetro lambda.

  • R es el flujo resultante que se construirá utilizando T. Sabiendo que tenemos muchas instancias de T, naturalmente tendremos muchos flujos de R. Todos estos flujos del tipo R ahora se combinarán en un solo flujo 'plano' del tipo R .

Ejemplo

Los ejemplos de Bachiri Taoufiq ven su respuesta aquí son simples y fáciles de entender. Solo por claridad, digamos que tenemos un equipo de desarrolladores:

dev_team = {dev_1,dev_2,dev_3}

, con cada desarrollador conociendo muchos idiomas:

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

Aplicando Stream.map () en dev_team para obtener los idiomas de cada desarrollador:

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

te dará esta estructura:

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

que es básicamente a List<List<Languages>> /Object[Languages[]]. ¡No es muy bonita, ni Java8!

con Stream.flatMap()usted puede 'aplanar' las cosas, ya que toma la estructura anterior
y la convierte {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}, que básicamente se puede usar como List<Languages>/Language[]/etc...

así que al final, tu código tendría más sentido así:

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 simplemente:

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

Cuándo usar map () y usar flatMap () :

  • Úselo map()cuando se supone que cada elemento de tipo T de su flujo debe asignarse / transformarse en un solo elemento de tipo R. El resultado es un mapeo de tipo (1 elemento inicial -> 1 elemento final) y un nuevo flujo de elementos de tipo R es regresado.

  • Úselo flatMap()cuando se supone que cada elemento de tipo T de su flujo se asigna / transforma a una Colección de elementos de tipo R. El resultado es una asignación de tipo (1 elemento inicial -> n elementos finales) . Estas colecciones se fusionan (o aplanan ) en una nueva secuencia de elementos de tipo R. Esto es útil, por ejemplo, para representar bucles anidados .

Pre Java 8:

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

Publicar Java 8

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

Mapa: este método toma una función como argumento y devuelve una nueva secuencia que consta de los resultados generados al aplicar la función pasada a todos los elementos de la secuencia.

Imaginemos que tengo una lista de valores enteros (1,2,3,4,5) y una interfaz de función cuya lógica es cuadrada del entero pasado. (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);

salida:-

[1, 4, 9, 16, 25]

Como puede ver, una salida es una nueva secuencia cuyos valores son cuadrados de valores de la secuencia de entrada.

[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: - Este método toma una función como argumento, esta función acepta un parámetro T como argumento de entrada y devuelve una secuencia del parámetro R como valor de retorno. Cuando esta función se aplica a cada elemento de esta secuencia, produce una secuencia de nuevos valores. Todos los elementos de estas nuevas secuencias generadas por cada elemento se copian en una nueva secuencia, que será un valor de retorno de este método.

Imaginemos que tengo una lista de objetos de estudiantes, donde cada estudiante puede optar por múltiples materias.

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

salida:-

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

Como puede ver, una salida es una nueva secuencia cuyos valores son una colección de todos los elementos de las secuencias devueltas por cada elemento de la secuencia de entrada.

[S1, S2, S3] -> [{"historia", "matemáticas", "geografía"}, {"economía", "biología"}, {"ciencia", "matemáticas"}] -> tomar materias únicas - > [economía, biología, geografía, ciencia, historia, matemáticas]

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

lalitbhagtani
fuente
podría marcar la diferencia si proporciona un código en lugar de simplemente proporcionar un enlace de documento
Charles-Antoine Fournel
11

.map es para mapeo A -> B

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

Convierte cualquier artículo Aen cualquier artículo B. Javadoc


.flatMap es para A -> Stream <B> concatenando

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

--1 convierte cualquier elemento Aen Stream< B>, luego --2 concatena todas las transmisiones en una transmisión (plana). Javadoc


Nota 1: Aunque el último ejemplo se distribuye en una secuencia de primitivas (IntStream) en lugar de una secuencia de objetos (Stream), todavía ilustra la idea de .flatMap.

Nota 2: a pesar del nombre, el método String.chars () devuelve ints. Entonces, la colección real será:, [100, 111, 103, 99, 97, 116] donde 100está el código de 'd', 111es el código de 'o'etc. Nuevamente, con fines ilustrativos, se presenta como [d, o, g, c, a, t].

epox
fuente
3
La mejor respuesta. Directo al grano con ejemplos
GabrielBB
1

Respuesta simple

La mapoperación se puede producir una Streamde Stream.EXStream<Stream<Integer>>

flatMapla operación solo producirá Streamalgo. EXStream<Integer>

Melad Basilius
fuente
0

También una buena analogía puede ser con C # si está familiarizado con. Básicamente C # Selectsimilar a Java mapy C # SelectManyJava flatMap. Lo mismo se aplica a Kotlin para colecciones.

Arsenio
fuente
0

Esto es muy confuso para principiantes. La diferencia básica es que mapemite un elemento para cada entrada en la lista y flatMapes básicamente una operación map+ flatten. Para ser más claro, use flatMap cuando necesite más de un valor, por ejemplo, cuando espera que un bucle devuelva matrices, flatMap será realmente útil en este caso.

He escrito un blog sobre esto, puedes consultarlo aquí .

Niraj Chauhan
fuente
¿ Qué tal esta simple explicación ?
Eugene
0

Transmita operaciones flatMapy mapacepte una función como entrada.

flatMapespera que la función devuelva una nueva secuencia para cada elemento de la secuencia y devuelve una secuencia que combina todos los elementos de las secuencias devueltas por la función para cada elemento. En otras palabras, con flatMap, para cada elemento de la fuente, la función creará múltiples elementos. http://www.zoftino.com/java-stream-examples#flatmap-operation

mapespera que la función devuelva un valor transformado y devuelve una nueva secuencia que contiene los elementos transformados. En otras palabras, con map, para cada elemento de la fuente, la función creará un elemento transformado. http://www.zoftino.com/java-stream-examples#map-operation

Arnav Rao
fuente
0

flatMap()También aprovecha la evaluación parcial diferida de flujos. Leerá la primera secuencia y solo cuando sea necesario, pasará a la siguiente secuencia. El comportamiento se explica en detalle aquí: ¿Se garantiza que flatMap sea vago?

SK
fuente
0

Si piensa map()como una iteración ( forbucle de un nivel ), flatmap()es una iteración de dos niveles (como un forbucle anidado ). (Ingrese cada elemento iterado foo, y haga foo.getBarList()e itere en eso barListnuevamente)


map(): tomar una secuencia, hacer algo con cada elemento, recopilar el resultado único de cada proceso, generar otra secuencia. La definición de "hacer algo funciona" está implícita. Si el procesamiento de cualquier elemento da como resultado null, nullse utiliza para componer la secuencia final. Entonces, el número de elementos en el flujo resultante será igual al número de flujo de entrada.

flatmap(): tome una secuencia de elementos / secuencias y una función (definición explícita), aplique la función a cada elemento de cada secuencia y recopile toda la secuencia intermedia resultante para que sea una secuencia mayor ("aplanamiento"). Si el procesamiento de cualquier elemento da como resultado null, se proporciona una secuencia vacía al paso final de "aplanamiento". El número de elementos en la secuencia resultante es el total de todos los elementos participantes en todas las entradas, si la entrada es varias secuencias.

WesternGun
fuente