El uso de Java Stream forEach poco a poco se va haciendo hueco entre los desarrolladores que utilizan programación funcional. El problema fundamentalmente que existe con el método forEach es que estamos muy acostumbrados a usar un bucle clásico forEach y nos funciona bastante bien .Así que da un poco de pereza cambiarlo . ¿ Aporta alguna ventaja el uso de un forEach a nivel de Streams o es lo mismo de siempre?. Vamos a verlo.
Java forEach clásico
Supongamos que disponemos de la clase Persona.
package com.arquitecturajava; public class Persona { private String nombre; private String apellidos; private int edad; public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public int getEdad() { return edad; } public void setEdad(int edad) { this.edad = edad; } public String getApellidos() { return apellidos; } public void setApellidos(String apellidos) { this.apellidos = apellidos; } public Persona(String nombre, String apellidos, int edad) { super(); this.nombre = nombre; this.apellidos = apellidos; this.edad = edad; } }
Podemos construir una lista de Personas en código:
Persona p1 = new Persona("juan", "sanchez", 20) Persona p2 = new Persona("ana", "gomez", 12); Persona p3 = new Persona("pedro", "gutierrez", 40); List<Persona> lista=Arrays.asList(p1,p2,p3);
Ahora es el momento de decidir como recorrer la lista. En principio lo más natural es recorrerla con un bucle forEach de Java:
for (Persona p:lista) { // formato clasico System.out.println(p.getNombre()); System.out.println(p.getApellidos()); System.out.println(p.getEdad()); }
El resultado se mostrará por la consola y estamos bastante contentos con el:
Java Stream forEach , una alternativa
Eso sí podemos hacer lo mismo y utilizar el método forEach de los streams:
lista.stream().forEach((p)-> { System.out.println(p.getNombre()); System.out.println(p.getApellidos()); System.out.println(p.getEdad()); });
El resultado no variará:
Es cierto que al convertirlo en Stream podemos tener acceso a operaciones como map o filter que le dan una mayor flexibilidad . Ahora bien esto no es lo importante ya que a nosotros únicamente nos interesa la estructura forEach.
¿No aporta o si aporta?
En estos momentos el uso de Streams no nos aporta demasiado. Sin embargo vamos a ver otro ejemplo complementario. Supongamos que tenemos que recorrer un diccionario con Personas (hashmap) con los siguientes elementos:
Map < String, Persona > mapa = new HashMap < String, Persona > (); mapa.put(p1.getNombre(), p1); mapa.put(p2.getNombre(), p2); mapa.put(p3.getNombre(), p3);
No es muy difícil recorrerlo con un bucle forEach:
for (String nombre: mapa.keySet()) { Persona pSeleccionada=mapa.get(nombre); System.out.println(pSeleccionada.getNombre()); System.out.println(pSeleccionada.getApellidos()); System.out.println(pSeleccionada.getEdad()); }
Bueno no es muy difícil , pero no queda muy claro y los datos salen desordenados .
Lo que estamos haciendo es acceder a la colección de keys o claves del diccionario .Una vez tenemos las claves accedemos a cada elemento del diccionario usando el método get y pasando la clave, de ahí que salgan desordenados. ¿Cómo sería esto con el uso de streams?. Vamos a verlo:
mapa.forEach((k,v)->{ System.out.println(v.getNombre()); System.out.println(v.getApellidos()); System.out.println(v.getEdad()); });
El código es mucho más natural ya que usa una sobrecarga del método forEach y nos permite acceder directamente a los valores de un diccionario.
Muchas veces nos olvidamos de todas la novedades que trae Java 8 a nivel de programación funcional para simplificarnos el trabajo. El uso de Java Stream forEach es uno de ellos.
basura
En el ultimo ejemplo no se especifica un uso para la variable k en la expresion lambda, que hace esta alli?:
mapa.forEach((k,v)–>{
System.out.println(v.getNombre());
System.out.println(v.getApellidos());
System.out.println(v.getEdad());});
FALLO DE LA PÁGINA: Aparecen caracteres como “Amp” perdidos en los ejemplos de código
resuelto 🙂
Un posible problema de usar los foreach de java 8 es que los objetos que manipules dentro de el deben de ser final aunque no explícitamente definidos con final. ?
pues no lo he revisado, todo depende de a que llamas manipulación ?
Estaba trabajando con un foreach y dentro tenia un método de creación de otro objeto a partir del que se esta iterando, a ese método le pasaba un parámetro el cual cambiaba antes de entrar al foreach, entonces marcaba un error de compilación, creo yo que implicitamente el método foreach espera que los objetos que van a interactuar dentro de las iteraciones no hayan cambiado su valor.
Saludos
si , tienes razón ahora lo te he entendido 🙂
Si, si no me equivoco dentro del Stream del forEach({ variable final }); sólo puede haber variables final. Me sorprendí ayer que intenté usarlo y me obligaba a que la variable sea final.
Axel
Si así es
Entonces no es muy útil si por ejemplo de una lista quieres quitar elementos, habría que seguir usando Iterator?
hay opciones como los metodos replacewith y removeif a nivel de Collections