La diferencia entre map () y flatMap ()

1. Información general

Las API map () y flatMap () provienen de lenguajes funcionales. En Java 8, puede encontrarlos en Optional, Stream y en CompletableFuture (aunque con un nombre ligeramente diferente).

Los flujos representan una secuencia de objetos, mientras que los opcionales son clases que representan un valor que puede estar presente o ausente. Entre otras operaciones agregadas, tenemos los métodos map () y flatMap () .

A pesar de que ambos tienen los mismos tipos de devolución , son bastante diferentes. Expliquemos estas diferencias analizando algunos ejemplos de corrientes y opcionales.

2. Mapa y mapa plano en opcionales

El método map () funciona bien con Opcional , si la función devuelve el tipo exacto que necesitamos:

Optional s = Optional.of("test"); assertEquals(Optional.of("TEST"), s.map(String::toUpperCase));

Sin embargo, en casos más complejos, es posible que recibamos una función que también devuelva un Opcional . En tales casos, el uso de map () conduciría a una estructura anidada, ya que la implementación de map () realiza un ajuste adicional internamente.

Veamos otro ejemplo para comprender mejor esta situación:

assertEquals(Optional.of(Optional.of("STRING")), Optional .of("string") .map(s -> Optional.of("STRING")));

Como podemos ver, terminamos con la estructura anidada Opcional . Aunque funciona, es bastante engorroso de usar y no proporciona ninguna seguridad nula adicional, por lo que es mejor mantener una estructura plana.

Eso es exactamente lo que flatMap () nos ayuda a hacer:

assertEquals(Optional.of("STRING"), Optional .of("string") .flatMap(s -> Optional.of("STRING")));

3. Mapa y mapa plano en arroyos

Ambos métodos funcionan de manera similar para Opcional .

El método map () envuelve la secuencia subyacente en una instancia de Stream , mientras que el método flatMap () permite evitar Stream anidado estructura.

En el siguiente ejemplo, map () produce un Stream que consta de los resultados de aplicar el método toUpperCase () a los elementos del Stream de entrada :

List myList = Stream.of("a", "b") .map(String::toUpperCase) .collect(Collectors.toList()); assertEquals(asList("A", "B"), myList);

map () funciona bastante bien en un caso tan simple, pero ¿qué pasa si tenemos algo más complejo como una lista de listas como entrada?

Vamos a ver cómo funciona:

List
    
      list = Arrays.asList( Arrays.asList("a"), Arrays.asList("b")); System.out.println(list);
    

Este fragmento imprime una lista de listas [[a], [b]].

Ahora, usemos un flatMap () :

System.out.println(list .stream() .flatMap(Collection::stream) .collect(Collectors.toList()));

El resultado de dicho fragmento se acoplará a [a, b].

T él flatMap () método se aplana primera entrada de la corriente de arroyos a una corriente de Cuerdas (para más información sobre el acoplamiento, consulte el artículo). A partir de entonces, funciona de manera similar al método map () .

4. Conclusión

Java 8 nos da la oportunidad de usar los métodos map () y flatMap () que originalmente se usaban en lenguajes funcionales.

Podemos invocarlos en Streams y Optionals. Estos métodos nos ayudan a obtener objetos mapeados aplicando la función de mapeo proporcionada.

Como siempre, puede consultar los ejemplos proporcionados en este artículo en GitHub.