Ir al contenido principal

Java 8 Iteradores pasivos (forEach) y Stream API




Antes de la funcion forEach, todos los iteradores en JAVA eran activos es decir, involucraban un ciclo for o un while que recorría la colección de datos hasta cumplir con cierta condición. Es decir que recorrer una colección se vería así;

List<String> names = new LinkedList<String>();
for (String name : names)
    System.out.println(name);

Con la introducción de forEach como una función en la interfaz Iterable, todas las clases que implementen iterable tiene añadida la función forEach y el siclo anterior se podría simplificar así:

names.forEach(name -> System.out.println(name));

como puedes ver es una sola linea de código y lo mas inusual es este símbolo -> lo que nos indica estamos lidiando con una expresión Lambda, si no sabes nada de expresiones lambda ingresa a este link que tiene un tutorial a prueba de... digamos que for dummies (https://programmingbabel.blogspot.com/2017/09/lambda-expressions-java.html).

La expresión lambda en paréntesis quiere decir que para la variable name ejecute una impresión de linea del valor de name. Como puedes ver se pueden realizar gran cantidad de operaciones sobre todos los miembros de una colección solo con el llamado de este método.

Pero llevemos esto un paso mas adelante, con la llegada de forEach, también vino una nueva clase llamada Stream(Corriente), La idea detrás de Stream no es copiar ni almacenar una colección para eso ya existe la colección, la idea es crear una corriente por la que corren todos los miembros de la colección y sobre los que voy a realizar una operación. para obtener el stream de una colección solo se debe llamar al método .stream() ya tendrás acceso a la corriente de datos proveniente de la colección.

List<String> names = new LinkedList<>();
long count = names.stream().filter(name -> name.startsWith("A")).count()

como puedes ver con .stream() ya tienes el stream, ahora sobre stream puedes realizar varias operaciones en este caso haremos un filtro de todos los elementos que cumplan con la expresión Lambda, name -> name.startsWith("A"). y como el resultado es otro stream podemos llamar otra función de stream que cuente el numero de elementos en el stream resultante.

Ahora te preguntaras porque usar stream si hacerlo con el método clásico seria mas claro.

long count = 0;
for (String name : names)
  {
    if (name.startsWith("A"))
        ++count;
  }

Bueno hay un buen motivo, paralelismo ejecutar este for implica que secuencial mente vas a recorrer cal colección un elemento tras otro, ahora con stream le puedes decir que haga lo mismo pero con múltiples hilos, stream se encargara de partir el arreglo en varios pedazos asignar cada parte a un hilo y obtener el resultado mucho mas rápido.

Para hacer esta gracia no llamaremos a stream sino a parallelStream(). el ejemplo anterior se vera así

names.parallelStream().filter(name -> name.startsWith("A")).count();

y ya!, con este llamado cada operación sobre el stream se ejecutara con hilos paralelos, es decir que el filtro lo harán varios hilos.

Para ilustrar esta funcionalidad imagina que tienes el deber de contar todos los números mayores de 100 de una colección de diez millones de números, esto lo puedes hacer de 3 formas.

public static void main(String[] args) {

Random r= new Random();
List<Integer> numeros=new ArrayList<>();
for(int i=0;i<10000000;i++){
numeros.add(r.nextInt());
}

// metodo 1
long start = System.currentTimeMillis();
 
long count = 0;
for (Integer numero: numeros)
  {
    if (numero>100)
        ++count;
  }
 System.out.println(count);
              
System.out.println("prueba 1 tiempo "System.currentTimeMillis() - start);



//metodo 2
start = System.currentTimeMillis();
System.out.println(numeros.parallelStream().filter(numeros-> numeros>100).count());
System.out.println("prueba 1 tiempo "System.currentTimeMillis() - start);


//metodo 3
start = System.currentTimeMillis();
System.out.println(numeros.parallelStream().filter(numeros-> numeros>100).count());
System.out.println("prueba 1 tiempo "System.currentTimeMillis() - start);

}
verifica los tiempos, la eficiencia cambiara dependiendo de la arquitectura de tu maquina, ¿se demoro menos con parallelStream?, ¿donde mas crees que puedas usar stream, forEach y paralelismo?.

si quieres saber mas de Paralelismo este post tiene lo que necesitas (https://programmingbabel.blogspot.com/2017/09/paralelismo-en-java-executors_9.html)  y algo mas avanzado (https://programmingbabel.blogspot.com/2017/09/el-fork-join-framework-java.html)




                

Comentarios

Entradas populares de este blog

Conectarse al LDAP (directorio activo) utilizando JAVA

Un LDAP es un sistema de autenticación estándar utilizado por muchas compañías para controlar el acceso a aplicaciones y recursos. Por lo general se espera que cualquier nueva aplicación haga uso del LDAP para realizar la autenticación y controlar los permisos en forma unificada, la seguridad es transversal a todos los procesos que realiza una organización. Una vez regado el cuento a lo que vinimos, como conectarse a un LDAP. Lo primero que debes saber es que no se requieren librerías adicionales, JAVA en su distribución estandar ya cuenta con todo lo que necesitas. primero tres siglas que tienes que tener en cuanta. CN  = Common Name OU  = Organizational Unit DC  = Domain Component Para conectarse primero necesitas es instanciar un Objeto de la clase LdapContext, este se encargara de manejar la conexión al LDAP y las peticiones que se hagan al mismo. por consiguiente necesitara que le entregues una serie de propiedades de conexión. Esto lo haras con un Map de la siguiente

Clases anónimas JAVA (Anonymous Classes)

Código fuente articulo: https://www.dropbox.com/s/pzw44ot0ji2metl/Lambda.zip?dl=0 Las Clases anónimas en JAVA son una solución rápida para implementar una clase que se va utilizar una vez y de forma inmediata. Por ejemplo el  EventHandler  para un botón se puede implementar en la misma asignación valiendonos de la interfaz  EventHandler  que ya esta definida. Pero mejor vamos con un ejemplo mas simple. De la definición anterior concluimos dos cosas la primera es que para crear una clase anónima es necesario haber definido una interfaz, una clase o una clase abstracta. La clase anónima lo que hará sera implementar la interfaz definida o sobre escribir los métodos definidos. Para ilustrar esto utilizaremos el ejemplo del JAVA Tutorial https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html . en este ejemplo tenemos que implementar clases que cumpliendo con la interfaz Saludo sean capaces de saludar en diferentes idiomas. El paso uno sera definir la int

Paralelismo en JAVA Executors (ExecutorService, Callables y Futures).

Código fuente articulo: https://www.dropbox.com/s/jci67120hmd0uce/Paralelismo.zip?dl=0 Para manejo de concurrencia Java desde la versión 5 presento el Concurrency API  este presento una mejora substancial en el manejo de hilos y procesos en paralelo, antes solo contabas con Thread y Runnable. lo que te obligaba a controlar la creación y el numero de hilos de ejecución, no te entregaba un resultado del procesamiento y no te dejaba controlar las Excepciones que lanzara un hilo...un hilo se lanzaba y amenos que le enviaras un un CallBack perdías todo contacto con el. Para solucionar este problema se crearon dos tipos de objetos Callables y Futures. estos dos te permiten encapsular una tarea asignándole un tipo de Objeto que sera el valor de retorno y hacer seguimiento a las tareas que ejecuto en paralelo pudiendo preguntar si ya termino, que resultado lanzo y que excepciones ocurrieron. Arranquemos con la implementación de Callable, Callable es una interfaz que te permite defini