Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprende Collect() Recopilando Elementos de Stream en una Colección | Operaciones Terminales en la Stream API
Stream API

bookCollect() Recopilando Elementos de Stream en una Colección

Ya estás familiarizado con las operaciones terminales e incluso las has utilizado en ejemplos y ejercicios anteriores. Ahora es momento de analizar más de cerca cómo funcionan. Primero, el método collect(), que es una de las operaciones terminales clave en la Stream API.

El método collect()

Es una de las herramientas más potentes al trabajar con streams, ya que permite acumular resultados en una List, Set o Map, así como realizar agrupamientos complejos y cálculos estadísticos.

Existen dos implementaciones del método collect()—vamos a explorar ambas.

Uso de collect() con interfaces funcionales

El método collect() en la Stream API puede utilizarse con tres interfaces funcionales para proporcionar control total sobre la recolección de datos:

  • Supplier<R> supplier – crea una colección vacía (R) donde se almacenarán los elementos. Por ejemplo, ArrayList::new inicializa una nueva lista;
  • BiConsumer<R, ? super T> accumulator – agrega elementos del stream (T) a la colección (R). Por ejemplo, List::add añade elementos a una lista;
  • BiConsumer<R, R> combiner – fusiona dos colecciones cuando se utiliza procesamiento en paralelo. Por ejemplo, List::addAll combina listas en una sola.

Los tres componentes trabajan juntos para proporcionar flexibilidad en la recolección de datos. Primero, el supplier crea una colección vacía que se utilizará para acumular los elementos del stream. Luego, el accumulator agrega cada elemento a medida que el stream los procesa. Este flujo se mantiene sencillo en un stream secuencial.

Sin embargo, al trabajar con streams paralelos (parallelStream()), el proceso se vuelve más complejo.

El procesamiento de datos se distribuye entre varios hilos, y cada hilo crea su colección separada. Una vez finalizado el procesamiento, estas colecciones individuales deben fusionarse en un único resultado. Aquí es donde interviene el combiner, combinando de manera eficiente las partes separadas en una colección unificada.

Ejemplo práctico

Trabaja para una tienda en línea y tiene una lista de productos. Su tarea es recopilar solo los productos que cuestan más de $500 utilizando el método collect() con tres parámetros.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { // Initial list of products List<Product> productList = List.of( new Product("Laptop", 1200.99), new Product("Phone", 599.49), new Product("Headphones", 199.99), new Product("Monitor", 299.99), new Product("Tablet", 699.99) ); // Filtering and collecting products over $500 using `collect()` List<Product> expensiveProducts = productList.parallelStream() .filter(product -> product.getPrice() > 500) // Keep only expensive products .collect( ArrayList::new, // Create a new list (list, product) -> list.add(product), // Add each product to the list ArrayList::addAll // Merge lists (if the stream is parallel) ); // Print the result System.out.print("Products over $500: " + expensiveProducts); } } class Product { private String name; private double price; Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public double getPrice() { return price; } @Override public String toString() { return name + " ($" + price + ")"; } }

El método collect() toma tres argumentos, cada uno definiendo un paso diferente para recopilar elementos en una lista:

  • ArrayList::new (Supplier) → crea un ArrayList<Product> vacío para almacenar los resultados;

  • (list, product) -> list.add(product) (BiConsumer) → agrega cada Product a la lista si cumple la condición de filtro (price > 500);

  • ArrayList::addAll (BiConsumer) → fusiona múltiples listas al usar streams paralelos, asegurando que todos los productos filtrados se combinen en una sola lista.

Aunque el tercer parámetro es principalmente para el procesamiento en paralelo, es requerido por collect().

Uso de collect() con la interfaz Collector

Además de trabajar con tres interfaces funcionales, el método collect() en la Stream API también puede utilizarse con implementaciones predefinidas de la interfaz Collector.

Este enfoque es más flexible y conveniente ya que proporciona métodos integrados para trabajar con colecciones.

La interfaz Collector<T, A, R> consta de varios métodos clave:

  • Supplier<A> supplier() – crea un contenedor vacío para acumular elementos;
  • BiConsumer<A, T> accumulator() – define cómo se agregan los elementos al contenedor;
  • BinaryOperator<A> combiner() – fusiona dos contenedores cuando se utiliza procesamiento en paralelo;
  • Function<A, R> finisher() – transforma el contenedor en el resultado final.

Como puedes observar, esta estructura es similar al método collect() que trabaja con interfaces funcionales, pero introduce el método finisher(). Este paso adicional permite realizar un procesamiento extra sobre los datos recopilados antes de devolver el resultado final—por ejemplo, ordenar la lista antes de retornarla.

Además, la interfaz Collector proporciona el método characteristics(), que define propiedades que ayudan a optimizar la ejecución de streams:

Estas características ayudan a que la Stream API optimice el rendimiento. Por ejemplo, si una colección es inherentemente no ordenada, especificar UNORDERED puede evitar ordenamientos innecesarios, haciendo la operación más eficiente.

Ejemplo práctico

Imagina que gestionas una tienda en línea y necesitas procesar los precios de los productos antes de recopilarlos. Por ejemplo, deseas redondear cada precio al número entero más cercano, eliminar duplicados y ordenar la lista final.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
package com.example; import java.util.*; import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<Double> prices = List.of(1200.99, 599.49, 199.99, 599.49, 1200.49, 200.0); // Using a custom `Collector` to round prices, remove duplicates, and sort List<Integer> processedPrices = prices.parallelStream() .collect(new RoundedSortedCollector()); System.out.println("Processed product prices: " + processedPrices); } } // Custom `Collector` that rounds prices, removes duplicates, and sorts them class RoundedSortedCollector implements Collector<Double, Set<Integer>, List<Integer>> { @Override public Supplier<Set<Integer>> supplier() { // Creates a `HashSet` to store unique rounded values return HashSet::new; } @Override public BiConsumer<Set<Integer>, Double> accumulator() { // Rounds price and adds to the set return (set, price) -> set.add((int) Math.round(price)); } @Override public BinaryOperator<Set<Integer>> combiner() { return (set1, set2) -> { set1.addAll(set2); // Merges two sets return set1; }; } @Override public Function<Set<Integer>, List<Integer>> finisher() { return set -> set.stream() .sorted() // Sorts the final list .toList(); } @Override public Set<Characteristics> characteristics() { // Order is not important during accumulation return Set.of(Characteristics.UNORDERED); } }

El procesamiento de los datos comienza al pasarlos a un Collector personalizado denominado RoundedSortedCollector.

Este recolector primero acumula todos los precios en un Set<Integer>, asegurando que los duplicados se eliminen automáticamente. Antes de agregar cada valor, redondea el precio utilizando Math.round(price) y lo convierte en un int. Por ejemplo, tanto 1200.99 como 1200.49 se convertirán en 1200, mientras que 199.99 se redondeará a 200.

Si el flujo se ejecuta en modo paralelo, el método combiner() fusiona dos conjuntos agregando todos los elementos de un conjunto en otro. Este paso es fundamental para entornos multihilo.

En la etapa final, después de recopilar todos los precios, el método finisher() transforma el conjunto en una lista ordenada. Convierte el Set<Integer> en un flujo, aplica sorted() para organizar los valores en orden ascendente y luego los recopila en una List<Integer>.

Como resultado, se obtiene una lista ordenada de precios únicos y redondeados que puede utilizarse para cálculos adicionales o fines de visualización.

1. ¿Qué hace el método collect() en Stream API?

2. ¿Qué capacidad adicional proporciona la interfaz Collector en comparación con collect() usando interfaces funcionales?

question mark

¿Qué hace el método collect() en Stream API?

Select the correct answer

question mark

¿Qué capacidad adicional proporciona la interfaz Collector en comparación con collect() usando interfaces funcionales?

Select the correct answer

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 3. Capítulo 1

Pregunte a AI

expand

Pregunte a AI

ChatGPT

Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla

Suggested prompts:

Can you explain the difference between using collect() with functional interfaces and with the Collector interface?

What are some common use cases for the collect() method in real-world applications?

Could you provide a simple code example demonstrating the use of collect() in Java?

Awesome!

Completion rate improved to 2.33

bookCollect() Recopilando Elementos de Stream en una Colección

Desliza para mostrar el menú

Ya estás familiarizado con las operaciones terminales e incluso las has utilizado en ejemplos y ejercicios anteriores. Ahora es momento de analizar más de cerca cómo funcionan. Primero, el método collect(), que es una de las operaciones terminales clave en la Stream API.

El método collect()

Es una de las herramientas más potentes al trabajar con streams, ya que permite acumular resultados en una List, Set o Map, así como realizar agrupamientos complejos y cálculos estadísticos.

Existen dos implementaciones del método collect()—vamos a explorar ambas.

Uso de collect() con interfaces funcionales

El método collect() en la Stream API puede utilizarse con tres interfaces funcionales para proporcionar control total sobre la recolección de datos:

  • Supplier<R> supplier – crea una colección vacía (R) donde se almacenarán los elementos. Por ejemplo, ArrayList::new inicializa una nueva lista;
  • BiConsumer<R, ? super T> accumulator – agrega elementos del stream (T) a la colección (R). Por ejemplo, List::add añade elementos a una lista;
  • BiConsumer<R, R> combiner – fusiona dos colecciones cuando se utiliza procesamiento en paralelo. Por ejemplo, List::addAll combina listas en una sola.

Los tres componentes trabajan juntos para proporcionar flexibilidad en la recolección de datos. Primero, el supplier crea una colección vacía que se utilizará para acumular los elementos del stream. Luego, el accumulator agrega cada elemento a medida que el stream los procesa. Este flujo se mantiene sencillo en un stream secuencial.

Sin embargo, al trabajar con streams paralelos (parallelStream()), el proceso se vuelve más complejo.

El procesamiento de datos se distribuye entre varios hilos, y cada hilo crea su colección separada. Una vez finalizado el procesamiento, estas colecciones individuales deben fusionarse en un único resultado. Aquí es donde interviene el combiner, combinando de manera eficiente las partes separadas en una colección unificada.

Ejemplo práctico

Trabaja para una tienda en línea y tiene una lista de productos. Su tarea es recopilar solo los productos que cuestan más de $500 utilizando el método collect() con tres parámetros.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { // Initial list of products List<Product> productList = List.of( new Product("Laptop", 1200.99), new Product("Phone", 599.49), new Product("Headphones", 199.99), new Product("Monitor", 299.99), new Product("Tablet", 699.99) ); // Filtering and collecting products over $500 using `collect()` List<Product> expensiveProducts = productList.parallelStream() .filter(product -> product.getPrice() > 500) // Keep only expensive products .collect( ArrayList::new, // Create a new list (list, product) -> list.add(product), // Add each product to the list ArrayList::addAll // Merge lists (if the stream is parallel) ); // Print the result System.out.print("Products over $500: " + expensiveProducts); } } class Product { private String name; private double price; Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public double getPrice() { return price; } @Override public String toString() { return name + " ($" + price + ")"; } }

El método collect() toma tres argumentos, cada uno definiendo un paso diferente para recopilar elementos en una lista:

  • ArrayList::new (Supplier) → crea un ArrayList<Product> vacío para almacenar los resultados;

  • (list, product) -> list.add(product) (BiConsumer) → agrega cada Product a la lista si cumple la condición de filtro (price > 500);

  • ArrayList::addAll (BiConsumer) → fusiona múltiples listas al usar streams paralelos, asegurando que todos los productos filtrados se combinen en una sola lista.

Aunque el tercer parámetro es principalmente para el procesamiento en paralelo, es requerido por collect().

Uso de collect() con la interfaz Collector

Además de trabajar con tres interfaces funcionales, el método collect() en la Stream API también puede utilizarse con implementaciones predefinidas de la interfaz Collector.

Este enfoque es más flexible y conveniente ya que proporciona métodos integrados para trabajar con colecciones.

La interfaz Collector<T, A, R> consta de varios métodos clave:

  • Supplier<A> supplier() – crea un contenedor vacío para acumular elementos;
  • BiConsumer<A, T> accumulator() – define cómo se agregan los elementos al contenedor;
  • BinaryOperator<A> combiner() – fusiona dos contenedores cuando se utiliza procesamiento en paralelo;
  • Function<A, R> finisher() – transforma el contenedor en el resultado final.

Como puedes observar, esta estructura es similar al método collect() que trabaja con interfaces funcionales, pero introduce el método finisher(). Este paso adicional permite realizar un procesamiento extra sobre los datos recopilados antes de devolver el resultado final—por ejemplo, ordenar la lista antes de retornarla.

Además, la interfaz Collector proporciona el método characteristics(), que define propiedades que ayudan a optimizar la ejecución de streams:

Estas características ayudan a que la Stream API optimice el rendimiento. Por ejemplo, si una colección es inherentemente no ordenada, especificar UNORDERED puede evitar ordenamientos innecesarios, haciendo la operación más eficiente.

Ejemplo práctico

Imagina que gestionas una tienda en línea y necesitas procesar los precios de los productos antes de recopilarlos. Por ejemplo, deseas redondear cada precio al número entero más cercano, eliminar duplicados y ordenar la lista final.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
package com.example; import java.util.*; import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<Double> prices = List.of(1200.99, 599.49, 199.99, 599.49, 1200.49, 200.0); // Using a custom `Collector` to round prices, remove duplicates, and sort List<Integer> processedPrices = prices.parallelStream() .collect(new RoundedSortedCollector()); System.out.println("Processed product prices: " + processedPrices); } } // Custom `Collector` that rounds prices, removes duplicates, and sorts them class RoundedSortedCollector implements Collector<Double, Set<Integer>, List<Integer>> { @Override public Supplier<Set<Integer>> supplier() { // Creates a `HashSet` to store unique rounded values return HashSet::new; } @Override public BiConsumer<Set<Integer>, Double> accumulator() { // Rounds price and adds to the set return (set, price) -> set.add((int) Math.round(price)); } @Override public BinaryOperator<Set<Integer>> combiner() { return (set1, set2) -> { set1.addAll(set2); // Merges two sets return set1; }; } @Override public Function<Set<Integer>, List<Integer>> finisher() { return set -> set.stream() .sorted() // Sorts the final list .toList(); } @Override public Set<Characteristics> characteristics() { // Order is not important during accumulation return Set.of(Characteristics.UNORDERED); } }

El procesamiento de los datos comienza al pasarlos a un Collector personalizado denominado RoundedSortedCollector.

Este recolector primero acumula todos los precios en un Set<Integer>, asegurando que los duplicados se eliminen automáticamente. Antes de agregar cada valor, redondea el precio utilizando Math.round(price) y lo convierte en un int. Por ejemplo, tanto 1200.99 como 1200.49 se convertirán en 1200, mientras que 199.99 se redondeará a 200.

Si el flujo se ejecuta en modo paralelo, el método combiner() fusiona dos conjuntos agregando todos los elementos de un conjunto en otro. Este paso es fundamental para entornos multihilo.

En la etapa final, después de recopilar todos los precios, el método finisher() transforma el conjunto en una lista ordenada. Convierte el Set<Integer> en un flujo, aplica sorted() para organizar los valores en orden ascendente y luego los recopila en una List<Integer>.

Como resultado, se obtiene una lista ordenada de precios únicos y redondeados que puede utilizarse para cálculos adicionales o fines de visualización.

1. ¿Qué hace el método collect() en Stream API?

2. ¿Qué capacidad adicional proporciona la interfaz Collector en comparación con collect() usando interfaces funcionales?

question mark

¿Qué hace el método collect() en Stream API?

Select the correct answer

question mark

¿Qué capacidad adicional proporciona la interfaz Collector en comparación con collect() usando interfaces funcionales?

Select the correct answer

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 3. Capítulo 1
some-alt