Java 8 - Ejemplos de Stream.map()

En este tutorial, hemos repasado ejemplos de cómo usar el método Stream.map() en Java 8. Hemos incluido un repaso sobre flujos y pasamos a ejemplos prácticos.

Introducción

Un Stream es una secuencia de objetos que admite muchos métodos diferentes que se pueden combinar para producir el resultado deseado.

Se pueden crear a partir de numerosas fuentes de datos, que suelen ser colecciones, pero también pueden ser canales de E/S, “matrices”, tipos de datos primitivos, etc.

Es importante enfatizar que un flujo no es una estructura de datos, ya que no almacena ningún dato. Es solo un envoltorio de fuente de datos que nos permite operar con datos más rápido, más fácil y con un código más limpio.

Una secuencia tampoco modifica nunca la estructura de datos original, solo devuelve el resultado de los métodos canalizados.

Tipos de transmisiones

Los flujos pueden ser secuenciales (creados con stream()), o paralelos (creados con parallelStream()). Los flujos paralelos pueden operar en múltiples subprocesos, mientras que los flujos secuenciales no pueden.

Las operaciones en flujos pueden ser intermedias o terminales:

Las operaciones intermedias en flujos devuelven un flujo. Es por eso que podemos encadenar múltiples operaciones intermedias sin usar punto y coma. Las operaciones intermedias incluyen los siguientes métodos:

  • map(op) - Devuelve una nueva secuencia en la que la función op proporcionada se aplica a cada uno de los elementos de la secuencia original
  • filter(cond) - Devuelve una nueva secuencia que solo contiene los elementos de la secuencia original que satisfacen la condición cond, especificada por un predicado
  • sorted() - Devuelve el flujo original, pero con los elementos ordenados

Las operaciones de Terminal se anulan o devuelven un resultado que no es de transmisión. Se llaman terminal porque no podemos encadenar más operaciones de transmisión una vez que hayamos usado una terminal, sin crear una nueva transmisión a partir de ella y comenzar de nuevo.

Algunas de las operaciones del terminal son:

  • collect() - Devuelve el resultado de las operaciones intermedias realizadas en el flujo original
  • forEach() - Un método void utilizado para iterar a través de la secuencia
  • reduce() - Devuelve un solo resultado producido a partir de una secuencia completa de elementos en el flujo original

En este tutorial, repasaremos la operación map() y cómo podemos usarla con Streams para convertir/asignar objetos de varios tipos.

Stream.map() Ejemplos

Echemos un vistazo a un par de ejemplos y veamos qué podemos hacer con la operación map().

Flujo de enteros a Flujo de Cadenas

1
2
3
Arrays.asList(1, 2, 3, 4).stream()
        .map(n -> "Number " + String.valueOf(n))
        .forEach(n -> System.out.println(n + " as a " + n.getClass().getName()));

Aquí, hicimos una lista de enteros y llamamos stream() en la lista para crear una nueva secuencia de datos. Luego, hemos mapeado cada número n en la lista, a través del método map(), a una cadena. Los Strings simplemente consisten en "Number" y String.valueOf(n).

Entonces, para cada número en nuestra lista original, ahora tendremos una cadena "Número n" correspondiente.

Dado que map() devuelve un Stream de nuevo, hemos usado el método forEach() para imprimir cada elemento en él.

Ejecutar este código da como resultado:

1
2
3
4
Number 1 as a java.lang.String
Number 2 as a java.lang.String
Number 3 as a java.lang.String
Number 4 as a java.lang.String

Nota: Esto no ha alterado la lista original en lo más mínimo. Simplemente procesamos los datos e imprimimos los resultados. Si quisiéramos persistir en este cambio, recopilaríamos () los datos nuevamente en un objeto Colección como Lista, Mapa, Conjunto, etc.:

1
2
3
4
5
6
7
8
List<Integer> list = Arrays.asList(1, 2, 3, 4);
        
List<String> mappedList = list.stream()
        .map(n -> "Number " + String.valueOf(n))
        .collect(Collectors.toList());

System.out.println(list);
System.out.println(mappedList);

Esto resulta en:

1
2
[1, 2, 3, 4]
[Number 1, Number 2, Number 3, Number 4]

Flujo de cadenas en flujo de enteros

Ahora, hagámoslo al revés: convierta un flujo de cadenas en un flujo de enteros:

1
2
3
Arrays.asList("1", "2", "3", "4").stream()
        .map(n -> Integer.parseInt(n))
        .forEach(n -> System.out.println(n));

Como era de esperar, este código producirá el siguiente resultado:

1
2
3
4
1
2
3
4

Lista de objetos en lista de otros objetos

Digamos que tenemos una clase Punto que representa un punto en un sistema de coordenadas cartesianas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Point {
    int X;
    int Y;
    
    Point(int x, int y){
        this.X=x;
        this.Y=y;
    }
    
    @Override
    public String toString() {
        return "(" + this.X + ", " + this.Y + ")";
    }
    
}

Luego, supongamos que queremos escalar una determinada forma por un factor de 2, esto significa que tenemos que tomar todos los puntos que tenemos y duplicar sus valores X e Y. Esto se puede hacer asignando los valores originales a sus contrapartes escaladas.

Luego, dado que nos gustaría usar las nuevas coordenadas, recopilaremos estos datos en una nueva lista de puntos escalados:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
List<Point> originalPoints = Arrays.asList(new Point(1, 2),
                                           new Point(3, 4),
                                           new Point(5, 6),
                                           new Point(7, 8));
System.out.println("Original vertices: " + originalPoints);
    
List<Point> scaledPoints = originalPoints
        .stream()
        .map(n -> new Point(n.X * 2, n.Y * 2))
        .collect(Collectors.toList());

System.out.println("Scaled vertices: " + scaledPoints);

Este ejemplo producirá el siguiente resultado:

1
2
Original vertices: [(1, 2), (3, 4), (5, 6), (7, 8)]
Scaled vertices: [(2, 4), (6, 8), (10, 12), (14, 16)]

Conclusión

En este artículo, explicamos qué flujos hay en Java. Mencionamos algunos de los métodos básicos que se usan en los flujos y nos enfocamos específicamente en el método map() y cómo se puede usar para la manipulación de flujos.

Es importante mencionar que las secuencias no son realmente una parte obligatoria de la programación, sin embargo, son más expresivas y pueden mejorar significativamente la legibilidad de su código, razón por la cual se convirtieron en una práctica de programación común.

Licensed under CC BY-NC-SA 4.0