miércoles, 26 de julio de 2017

Sobre Stream API en Java 8

Stream API en Java8 se utiliza para trabajar con las colecciones, escribiendo el código en el estilo funcional. Gracias a su simplicidad se ha hecho muy popular entre los programadores. Segun la descripcion oficial, la bilioteca «Java.util.stream» - nos permite hacer varias operaciones de estilo funcional con las colecciones de elementos, tales como transformaciones mapReduce, etc. Contiene las operaciones "intermedias", como Stream.map o Stream.filter, pero también las operaciones "finales" como Stream.forEach o Stream.reduce . Las operaciones "intermedias" no serán ejecutadas hasta q una operacion "final" sera llamada (Stream.map no sera ejecutado hasta Stream.reduce, tb si lo metes antes en el código). Ese comportamiento se llama LAZY.
Otra gran característica de Stream API es la paralelización de las fuentes parallelStream (). Se utiliza para mejorar el rendimiento cuando tienes q procesar grandes cantidades de datos. Las corrientes paralelas pueden acelerar la ejecución para ciertos tipos de transacciones. Yo las uso, cuando sé que la colección es demasiado grande para manejarla con «ForkJoin». Más información sobre la biblioteca ForkJoin puedes leer aqui: http://www.baeldung.com/java-fork-join.
Volvemos a nuestras ovejas y vamos a considerar un ejemplo práctico. Un ejemplo para buscar máximos y mínimos en una colección.

ArrayList testValues = new ArrayList();
testValues.add(0,15);
testValues.add(1,1);
testValues.add(2,2);
testValues.add(3,100);
testValues.add(4,50);
Optional
maxValue = testValues.stream().max(Integer::compareTo);
System.out.println("MaxValue="+maxValue);
Optional
minValue = testValues.stream().min(Integer::compareTo);
System.out.println("MinValue="+minValue); 




Ejemplo poco mas complicado donde añadimos una excepción (cuando el valor max será null):

ArrayList testValuesNull = new ArrayList();
testValuesNull.add(0,null);
testValuesNull.add(1,1);
testValuesNull.add(2,2);
testValuesNull.add(3,70);
testValuesNull.add(4,50);
Optional maxValueNotNull = testValuesNull.stream().filter((p) -> p != null).max(Integer::compareTo);
System.out.println("maxValueNotNull="+maxValueNotNull);



Vamos a ir más para allá y crear una colección q se llama "campo de deportes", que consiste en los campos "Nombre" y "Número de días en el campo de deportes."

public class SportsCamp 

{ 
   private String name;//nombre 
   private Integer day;//numero de dias    

   public SportsCamp(String name, int day) 
   { 
     this.name = name;      
     this.day = day; 
   } 
   public String getName() return name;
   public void setName(String name) { this.name = name; } 
   public Integer getDay() { return day; } 
   public void setDay(Integer day) { this.day = day; } 
}


Con los nuevos datos podemos hacer varias operaciones, p.e. obtenir la frecuencia, con cual un nombre aparece en la colección:

import java.util.Arrays;
import java.util.Collection;
public class Start
{
   public static void main(String[] args)
   {
     Collection<SportsCamp>
sport = Arrays.asList( new SportsCamp("Juan", 5), new SportsCamp("Pedro", 7), new SportsCamp("Isabel", 10) );
     String name = sport.stream().max((p1,p2) ->           p1.getDay().compareTo(p2.getDay())).get().getName();
     System.out.println("Name="+name);
   }
}



Este ejemplo devuelve el nombre de Isabel - es la q estará más tiempo en el campo de deportes. Como he dicho antes, las operaciones se dividen en "intermedias" y "finales". En el ejemplo de arriba podemos añadir un nuevo filtro por el nombre «Juan» y contamos, cuántas veces ocurre este nombre.

long countName = sport.stream().filter((p) -> p.getName() != null && p.getName().equals("Juan")).count();
System.out.println("countName="+countName);


Obviamente el resultado sera igual a 1 (hasta q añadimos otro "Juan" a nuestra colección).
Como he dicho anteriormente, podemos utilizar parallelStream() para contar los "Juanes":

long countNameParallel = sport.parallelStream().filter((p) -> p.getName() != null && p.getName().equals("Juan")).count();
System.out.println("countNameParallel=" + countNameParallel);

La eficacidad de parallelStream () se muestra mejor en los servidores de gama alta (multi-core) con grandes cantidades de datos. Es la cuestión de probar y ajustar los parámetros del servidor. 


De map/reduce vamos a mirar por el momento solo el reduce. El reduce se usa para "construir" componentes, en términos simples, si creemos producir una nueva instancia del objeto agregando otros elementos, el reduce lo hace bien. P.e. sumar todos los días de todos participantes en el campo de deportes.

Integer daySum = sport.stream().reduce(0, (sum, p) -> sum += p.getDay(), (sum1, sum2) -> sum1 + sum2);
System.out.println("DaySize=" + daySum);//resultado = 22

En esta forma de realización, el reduce toma tres valores: 1.identificador, 2.acumulador, 3.resultado.

No hay comentarios:

Publicar un comentario