collect(), toList(), toSet(), toMap()
La operación terminal collect() es una de las herramientas más versátiles en el API Stream de Java, permitiendo transformar los elementos de un stream en diferentes tipos de colecciones o valores. Esta operación funciona en conjunto con la interfaz Collector, que define cómo se acumularán los elementos procesados.
¿Te está gustando esta lección?
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
La sintaxis básica de collect() es:
<R, A> R collect(Collector<? super T, A, R> collector)
Donde:
- T es el tipo de elementos en el stream
- R es el tipo del resultado
- A es el tipo del acumulador intermedio
La clase Collectors proporciona implementaciones predefinidas que facilitan las operaciones más comunes. Veamos los colectores fundamentales:
Collector toList()
El método toList() convierte los elementos de un stream en una lista:
import java.util.List;
import java.util.stream.Collectors;
public class EjemploToList {
public static void main(String[] args) {
List<String> nombres = List.of("Ana", "Carlos", "Beatriz", "Daniel");
// Filtrar nombres que empiezan con 'A' o 'B' y convertirlos a lista
List<String> filtrados = nombres.stream()
.filter(n -> n.startsWith("A") || n.startsWith("B"))
.collect(Collectors.toList());
System.out.println("Nombres filtrados: " + filtrados);
}
}
Este código produce una lista con los elementos "Ana" y "Beatriz". A partir de Java 16, también podemos usar el método toList() directamente en el Stream:
List<String> filtrados = nombres.stream()
.filter(n -> n.startsWith("A") || n.startsWith("B"))
.toList(); // Método directo desde Java 16
La diferencia principal es que el método directo toList() devuelve una lista inmutable, mientras que Collectors.toList() devuelve una lista modificable.
Collector toSet()
El método toSet() convierte los elementos de un stream en un conjunto (Set), eliminando automáticamente los duplicados:
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class EjemploToSet {
public static void main(String[] args) {
List<String> palabras = List.of("Java", "Python", "Java", "JavaScript", "Python");
// Convertir a Set para eliminar duplicados
Set<String> lenguajesUnicos = palabras.stream()
.collect(Collectors.toSet());
System.out.println("Lenguajes únicos: " + lenguajesUnicos);
}
}
Este código produce un conjunto con los elementos "Java", "Python" y "JavaScript", eliminando los duplicados.
Collector toMap()
El método toMap() permite convertir los elementos de un stream en un mapa, especificando cómo extraer las claves y los valores:
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class Producto {
private String nombre;
private double precio;
public Producto(String nombre, double precio) {
this.nombre = nombre;
this.precio = precio;
}
public String getNombre() { return nombre; }
public double getPrecio() { return precio; }
}
public class EjemploToMap {
public static void main(String[] args) {
List<Producto> productos = List.of(
new Producto("Laptop", 1200.0),
new Producto("Teléfono", 800.0),
new Producto("Tablet", 500.0)
);
// Crear un mapa con nombre como clave y precio como valor
Map<String, Double> preciosPorProducto = productos.stream()
.collect(Collectors.toMap(
Producto::getNombre, // función para extraer la clave
Producto::getPrecio // función para extraer el valor
));
System.out.println("Precios por producto: " + preciosPorProducto);
}
}
Este código crea un mapa donde las claves son los nombres de los productos y los valores son sus precios.
Si existe la posibilidad de claves duplicadas, podemos proporcionar una función de resolución de conflictos:
Map<String, Double> preciosPorProducto = productos.stream()
.collect(Collectors.toMap(
Producto::getNombre,
Producto::getPrecio,
(precioExistente, precioNuevo) -> precioNuevo // En caso de duplicados, usar el nuevo valor
));
Otros colectores útiles
Además de los colectores básicos, Collectors ofrece métodos para realizar operaciones de agregación:
joining(): Concatena elementos de tipo String:
List<String> frutas = List.of("Manzana", "Banana", "Cereza");
String resultado = frutas.stream()
.collect(Collectors.joining(", "));
System.out.println("Frutas: " + resultado); // Imprime: Frutas: Manzana, Banana, Cereza
summingInt(), summingLong(), summingDouble(): Suman valores numéricos:
List<Producto> productos = List.of(
new Producto("Laptop", 1200.0),
new Producto("Teléfono", 800.0),
new Producto("Tablet", 500.0)
);
double precioTotal = productos.stream()
.collect(Collectors.summingDouble(Producto::getPrecio));
System.out.println("Precio total: " + precioTotal); // Imprime: Precio total: 2500.0
averagingInt(), averagingLong(), averagingDouble(): Calculan promedios:
double precioPromedio = productos.stream()
.collect(Collectors.averagingDouble(Producto::getPrecio));
System.out.println("Precio promedio: " + precioPromedio); // Imprime: Precio promedio: 833.3333...
counting(): Cuenta elementos:
long cantidad = productos.stream()
.collect(Collectors.counting());
System.out.println("Cantidad de productos: " + cantidad); // Imprime: Cantidad de productos: 3
maxBy(), minBy(): Encuentran el máximo o mínimo según un comparador:
import java.util.Comparator;
import java.util.Optional;
Optional<Producto> masBarato = productos.stream()
.collect(Collectors.minBy(Comparator.comparing(Producto::getPrecio)));
masBarato.ifPresent(p -> System.out.println("Producto más barato: " + p.getNombre()));
Colectores personalizados
También podemos crear colectores personalizados utilizando el método Collectors.collectingAndThen(), que permite aplicar una función final al resultado de otro colector:
List<Integer> numeros = List.of(1, 2, 3, 4, 5);
// Colectar a lista y convertir a array inmutable
Integer[] arrayNumeros = numeros.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(),
lista -> lista.toArray(new Integer[0])
));
La operación collect() junto con los colectores predefinidos proporciona una forma elegante y funcional de transformar streams en estructuras de datos útiles. Estos colectores simplifican operaciones que de otro modo requerirían código imperativo más extenso y propenso a errores.
Aprendizajes de esta lección
- Comprender la función y sintaxis de la operación terminal collect() en Streams.
- Utilizar colectores predefinidos como toList(), toSet() y toMap() para transformar streams en colecciones.
- Aplicar colectores para realizar operaciones de agregación como sumas, promedios y conteos.
- Crear y usar colectores personalizados con collectingAndThen() para transformar resultados.
- Diferenciar entre listas mutables e inmutables generadas por diferentes métodos de colectores.
Completa Java y certifícate
Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs