package ar.com.javacuriosities.streams; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Comparator; /* * La JDK provee muchas Terminal operations (average, sum, min, max, and count) que retornan * un valor combinando el contenido del stream. Este tipo de operaciones es llamado * reduction. * También encontraremos reducciones que retornan una collection en lugar de un solo valor, muchas * reducciones ejecutan una tarea especifica como obtener el average de una secuencia de elementos * o agrupar elementos en categorías. * Java provee métodos de propósito general como * * - Stream.reduce * - Stream.collect */ public class Lesson07Reductions { public static void main(String[] args) { try { /* * Para analizar el reduce usaremos el siguiente ejemplo, donde * debemos obtener la longitud de la línea mas larga, o sea tenemos * una secuencia con las líneas y queremos reducir esto a un valor * que contiene la longitud de la línea mas larga */ System.out.println("Finding the length of the longest line in a file"); URL resource = Thread.currentThread().getContextClassLoader().getResource("data/lines.txt"); Path input = Paths.get(resource.toURI()); // Se obtiene la longitud de cada línea y luego el máximo del stream generado int longestLineLength = Files.lines(input).mapToInt(String::length).max().getAsInt(); System.out.println("The size of the longest line is: " + longestLineLength); /* * La solución anterior es simple y nos permite cumplir nuestro objetivo pero imaginemos * que tenemos que obtener la línea mas larga en lugar de su longitud. * La siguiente implementación es una forma simple de hacerlo su mayor problema radica * en que debemos ordenar el stream para quedarnos con la linea mas larga, lo cual * no es bueno si nuestro stream es muy grande */ System.out.println("Find the longest line in a file"); String longestLine = Files.lines(input).sorted((x, y) -> y.length() - x.length()).findFirst().get(); System.out.println("Version 1- The longest line is: " + longestLine); /* * Si analizamos el ejercicio y pensamos en forma imperativa veremos que la forma * de hacer esto es mediante un loop donde en la iteración de cada línea preguntamos su * longitud y la comparamos con la línea mas larga hasta el momento, de aquí podemos * extraer los dos siguientes conceptos * * - External loops (El ciclo que ejecutamos para recorrer cada línea) * - Keep mutable state (La variable que va conteniendo la línea mas larga) * * Dado que Java 8 incluye todo este gran set de herramienta para programación funcional * veamos como resolver esto de una forma que no sea imperativa (Esto no quiere decir * que el estilo funcional siempre sea el correcto, siempre debemos analizar nuestras opciones * y ver que nos conviene aplicar) * * Si vemos de Stream API nos encontraremos con un método llamado reduce(), el cual * toma un valor parcial y el siguiente valor de nuestro stream para retornar el siguiente * resultado parcial así hasta finalizar el stream, si analizamos esto veremos que esto * tiene varias ventajas * * - Internal loops (Ahora el recorrido es realizado por Stream API lo que me permite una forma fácil de paralizar) * - No mutable state (Por que el acumulador es siempre recibido por la función y se retorna el mismo o uno nuevo) * - Laziness (Iremos consumiendo línea por línea sin tener todas en memoria) */ longestLine= Files.lines(input).reduce((acc, curr) -> { if (acc.length() > curr.length()) { return acc; } return curr; }).get(); System.out.println("Version 2- The longest line is: " + longestLine); // Otra solución posible es usando una version especializada de max() longestLine = Files.lines(input).max(Comparator.comparingInt(String::length)).get(); System.out.println("Version 3- The longest line is: " + longestLine); } catch (Exception e) { // Log and Handle exception e.printStackTrace(); } } }