De null a full

Programación Java

View on GitHub

Stream

La clase Stream<T> es un procesador de secuencias de elementos que aplica operaciones en modo funcional.

Es importante tener en cuenta que los streams son de uso único, e intentar reutilizarlos provocará lanzamiento de excepciones o efectos secundarios silenciosos; esto se debe a que los streams no han sido diseñados para guardar elementos.

Los streams se evalúan de manera vaga, lo que quiere decir que las operaciones definidas en un Stream no se ejecutan hasta que se llame a una operación terminal.

Crear objetos Stream

Streams concurrentes

Permitir habilitar la ejecución en paralelo de Stream basta con llamar al método parallelStream().

Operaciones intermedias

Operaciones terminales

Estos métodos retornan objetos que no son Stream; para ello, se ejecutan todas las operaciones definidas en este.

Reduce

reduce agrupa los elementos del stream combinándolos sobre sí mismos.

Los elementos a tener en cuenta en una reducción son:

Las implementaciones de reduce son

Collect

collect(Collector<? super T, A, R> collector) recoge los elementos del stream en una estructura de datos.

La interfaz Collector, instanciable como clase anónima o mediante Collector.of, debe implementar los siguientes métodos:

collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) genera un colector asumiendo que el finalizador es una función identidad i -> i que hace que el mismo acumulador sea el resultado. Este colector no usa características.

collect actualiza el estado del acumulador en lugar de sustituirlo. dado que Java siempre hace paso de variables por valor, esto significa que no podemos trabajar con tipos simples como Integer o String; para ello tendremos que utilizar reduce o envolver nuestro tipo de alguna manera (por ejemplo, usando AtomicInteger o StringBuilder).

Collectors

Para hacer más fácil el trabajo de generar colectores, la clase Collectors implementa varios métodos que simplifican el proceso.

Para agrupar en un String, joining une strings (u otros CharSequence); podemos pasarle separador entre elementos, prefijo y sufijo.

Para agrupar en estructuras de datos:

Existen otros colectores como accesos alternativos a funciones de Stream (p.e. stream.collect(Collectors.counting()) equivale a stream.count(); aunque parece redundante y es recomenable evitar estos colectores por legibilidad, estos nos permiten encapsular lógica más comodamente si es necesario (por ejemplo, una función que reciba un colector según el que retorne el máximo, el mínimo o la media de una lista de números).

Streams de primitivos

Si queremos trabajar con primitivos, disponemos de las interfaces IntStream, LongStream y DoubleStream.

Estas clases ponen a nuestra disposición, además de las maneras ya indicadas de crear un stream, los métodos range y rangeClosed para generar streams con rangos numéricos. Stream también tiene métodos mapToX y flatMapToX para generarlos a partir de streams normales.

Un stream de primitivos puede convertirse en uno normal a través de los métodos boxed o mapToObj.

Ejercicios

Utilizando la API de Stream:

Enlaces de interés