Type something to search...
Transformando Colecciones con Java Streams: 15 Métodos Esenciales

Transformando Colecciones con Java Streams: 15 Métodos Esenciales

Introducción

En el mundo de Java, trabajar con colecciones de datos solía ser sinónimo de bucles interminables, condicionales anidados y código repetitivo. Pero con la llegada de Java Streams (desde Java 8), todo cambió. Los Streams introdujeron un paradigma funcional y declarativo que permite manipular datos de manera eficiente, legible y elegante.

¿Imaginas poder filtrar, transformar, agrupar o reducir elementos con solo unas líneas de código? Los métodos de los Streams hacen esto posible, convirtiendo operaciones complejas en secuencias intuitivas. Pero para aprovecharlos al máximo, es clave conocer sus herramientas principales.

Aquí te presentamos un listado detallado de los métodos más poderosos de los Streams, divididos en dos categorías: métodos generales y métodos de agrupación. Descubre cómo dominarlos puede simplificar tu código, potenciar tu productividad y desbloquear nuevas posibilidades en el manejo de datos.


Listado de Métodos de Java Streams

Métodos Generales

  1. filter(Predicate<? super T> predicate)
    Filtra los elementos que cumplen con una condición.
    List<Integer> numeros = Arrays.asList(5, 12, 3, 20); 
    List<Integer> mayoresA10 = numeros.stream() 
    								  .filter(x -> x > 10) 
    								  .collect(Collectors.toList()); 
    // Resultado: [12, 20] 
  2. map(Function<? super T, ? extends R> mapper)
    Transforma cada elemento aplicando una función.
    List<String> palabras = Arrays.asList("java", "streams"); 
    List<Integer> longitudes = palabras.stream() 
    								   .map(String::length) 
    								   .collect(Collectors.toList()); 
    // Resultado: [4, 7] 
  3. flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
    Aplana múltiples Streams en uno solo (útil para listas anidadas).
    List<List<Integer>> listaAnidada = Arrays.asList( 
    	Arrays.asList(1, 2), 
    	Arrays.asList(3, 4) 
    ); 
    List<Integer> listaPlana = listaAnidada.stream() 
    									   .flatMap(List::stream) 
    									   .collect(Collectors.toList()); 
    // Resultado: [1, 2, 3, 4] 
  4. reduce(BinaryOperator accumulator)
    Reduce los elementos a un único valor mediante una operación (ej: suma).
    List<Integer> numeros = Arrays.asList(1, 2, 3, 4); 
    Optional<Integer> suma = numeros.stream() 
    								.reduce((a, b) -> a + b); 
    // Resultado: 10 
  5. collect(Collector<? super T, A, R> collector)
    Transforma el Stream en una colección o estructura de datos.
    List<String> palabras = Arrays.asList("a", "b", "c"); 
    Set<String> set = palabras.stream() 
    						  .collect(Collectors.toSet()); 
    // Resultado: [a, b, c] (como Set) 
  6. forEach(Consumer<? super T> action)
    Ejecuta una acción en cada elemento (como imprimirlo).
    List<String> frutas = Arrays.asList("Manzana", "Pera"); 
    frutas.stream() 
    	  .forEach(fruta -> System.out.print(fruta + " ")); 
    // Resultado: "Manzana Pera " 
  7. sorted()
    Ordena los elementos (requiere que sean comparables).
    List<Integer> numeros = Arrays.asList(3, 1, 4, 2); 
    List<Integer> ordenados = numeros.stream() 
    								 .sorted() 
    								 .collect(Collectors.toList()); 
    // Resultado: [1, 2, 3, 4] 
  8. distinct()
    Elimina duplicados, retornando elementos únicos.
    List<Integer> numeros = Arrays.asList(2, 2, 5, 5); 
    List<Integer> unicos = numeros.stream() 
    							  .distinct() 
    							  .collect(Collectors.toList()); 
    // Resultado: [2, 5] 
  9. limit(long maxSize)
    Limita el Stream a un número máximo de elementos.
    List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5); 
    List<Integer> primeros3 = numeros.stream() 
    								  .limit(3) 
    								  .collect(Collectors.toList()); 
    // Resultado: [1, 2, 3] 
  10. skip(long n)
    Omite los primeros n elementos del Stream.
    List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5); 
    List<Integer> sinPrimeros2 = numeros.stream() 
    									.skip(2) 
    									.collect(Collectors.toList()); 
    // Resultado: [3, 4, 5] 

Métodos para Agrupar Elementos

  1. Collectors.groupingBy(Function<? super T, ? extends K> classifier)
    Agrupa elementos por una clave (ej: edad de una persona).
    List<Persona> personas = Arrays.asList( 
    	new Persona("Ana", 25), 
    	new Persona("Luis", 25) 
    ); 
    Map<Integer, List<Persona>> porEdad = personas.stream() 
    	.collect(Collectors.groupingBy(Persona::getEdad)); 
    // Resultado: {25=[Ana, Luis]} 
  2. Collectors.partitioningBy(Predicate<? super T> predicate)
    Divide el Stream en dos grupos: los que cumplen y no cumplen un predicado.
    // Agrupar por categoría + stock mayor a 5 
    Map<String, List<Producto>> porCategoriaYStock = productos.stream() 
    	.collect(Collectors.groupingBy(p -> 
    		p.getCategoria() + "-" + (p.getStock() > 5 ? "AltoStock" : "BajoStock") 
    	)); 
    /* 
    Resultado: 
    { 
      "Electrónica-AltoStock": [Laptop, Smartphone], 
      "Ropa-AltoStock": [Camisa] 
    } 
    */ 
  3. Collectors.groupingBy(classifier, downstream)
    Agrupa y luego aplica un segundo colector a cada grupo (ej: contar elementos).
    // Precio promedio por categoría 
    Map<String, Double> precioPromedio = productos.stream() 
    	.collect(Collectors.groupingBy( 
    		Producto::getCategoria, 
    		Collectors.averagingDouble(Producto::getPrecio) 
    	)); 
    /* 
    Resultado: 
    { 
      "Electrónica": 1000.0, 
      "Ropa": 40.0 
    } 
    */ 
  4. Collectors.groupingBy(classifier, mapFactory, downstream)
    Agrupa usando un tipo de mapa específico (ej: TreeMap).
    // Agrupar por rangos de precios 
    Map<String, List<Producto>> porRangoPrecio = productos.stream() 
    	.collect(Collectors.groupingBy(p -> { 
    		if (p.getPrecio() < 100) return "Económico"; 
    		else if (p.getPrecio() < 1000) return "Medio"; 
    		else return "Premium"; 
    	})); 
    /* 
    Resultado: 
    { 
      "Premium": [Laptop], 
      "Medio": [Smartphone], 
      "Económico": [Camisa] 
    } 
    */ 
  5. Agrupar con downstream complejo (ej: contar y sumar stock)
    // Por categoría: cantidad de productos y stock total 
    Map<String, Map<String, Object>> estadisticas = productos.stream() 
    	.collect(Collectors.groupingBy( 
    		Producto::getCategoria, 
    		Collectors.collectingAndThen( 
    			Collectors.toList(), 
    			lista -> { 
    				int cantidad = lista.size(); 
    				int stockTotal = lista.stream().mapToInt(Producto::getStock).sum(); 
    				return Map.of("Cantidad", cantidad, "Stock Total", stockTotal); 
    			} 
    		) 
    	)); 
    /* 
    Resultado: 
    { 
      "Electrónica": {"Cantidad": 2, "Stock Total": 15}, 
      "Ropa": {"Cantidad": 1, "Stock Total": 20} 
    } 
    */ 

Conclusión

Dominar los métodos de Java Streams no solo simplifica tu código, sino que también mejora su legibilidad y eficiencia. Al explorar y aplicar estos métodos, descubrirás nuevas formas de manipular colecciones de datos que pueden transformar tu enfoque en el desarrollo de software. ¡Sigue profundizando y experimentando con Java Streams para desbloquear todo su potencial!

Related Posts

Optimizando el Acceso a Datos: La Importancia de las Proyecciones JPA en Spring Boot

Optimizando el Acceso a Datos: La Importancia de las Proyecciones JPA en Spring Boot

El Costo Oculto de Traer Demasiada Información En el desarrollo de aplicaciones que interactúan con bases de datos, una tarea fundamental es la recuperación de datos. Al usar Object-Relational Map

Leer más
Desmitificando Gradle: El Primer Paso para Automatizar tu Mundo Java

Desmitificando Gradle: El Primer Paso para Automatizar tu Mundo Java

En el vertiginoso universo del desarrollo de software, la eficiencia no es un lujo, es una necesidad. Dedicar tiempo a tareas repetitivas como compilar código, ejecutar pruebas, empaquetar la aplicaci

Leer más
Del Dicho al Hecho: Generando Proyectos Java con Plantillas y FreeMarker

Del Dicho al Hecho: Generando Proyectos Java con Plantillas y FreeMarker

En el artículo anterior, alcanzamos un hito crucial: construimos un plugin binario funcional en Java, completo con su propia configuración y tarea. Nuestro plugin "saludador" demostró que dominamos la

Leer más
De Consumidor a Creador: Construyendo tu Primer Plugin Binario de Gradle

De Consumidor a Creador: Construyendo tu Primer Plugin Binario de Gradle

En nuestro artículo anterior, desmitificamos Gradle y sentamos las bases para entender su funcionamiento. Aprendimos a crear proyectos, ejecutar tareas y comprendimos el rol fundamental de los plugins

Leer más
Diseñando un Wrapper de Respuesta en Java con Funcionalidades de Optional y Gestión de Estado

Diseñando un Wrapper de Respuesta en Java con Funcionalidades de Optional y Gestión de Estado

En el desarrollo de aplicaciones Java, el manejo de respuestas a solicitudes —especialmente aquellas que involucran operaciones asincrónicas, procesamiento de datos o comunicación con servicios extern

Leer más
Una Propuesta para Estandarizar la Seguridad en APIs REST con Arquitectura Hexagonal y Spring Security

Una Propuesta para Estandarizar la Seguridad en APIs REST con Arquitectura Hexagonal y Spring Security

En el desarrollo de aplicaciones empresariales modernas, la seguridad es un pilar fundamental. Sin embargo, lograr una arquitectura de seguridad que sea reutilizable, desacoplada y, al mismo t

Leer más
Manejando el Caos: Una Guía Definitiva sobre Excepciones de Dominio 🎯

Manejando el Caos: Una Guía Definitiva sobre Excepciones de Dominio 🎯

En el desarrollo de software moderno, escribir código que funciona perfectamente en escenarios ideales es solo el primer paso. La verdadera fortaleza de un sistema se manifiesta cuando debe enfrentar

Leer más
El Arte de Conectar: Forjando un DataSource Dinámico en Spring Boot

El Arte de Conectar: Forjando un DataSource Dinámico en Spring Boot

En el vertiginoso universo del desarrollo de software, donde la seguridad es un pilar innegociable y la agilidad es la moneda de cambio, nos enfrentamos a desafíos que van más allá de la simple lógica

Leer más
El Arte del Contexto: Diseño Flexible en Arquitecturas DDD con Java y Spring Boot

El Arte del Contexto: Diseño Flexible en Arquitecturas DDD con Java y Spring Boot

En el universo del desarrollo de software empresarial, nos enfrentamos a un dilema constante: cómo manejar información transversal —ese rastro de datos vitales como IDs de correlación, información del

Leer más
El Arte de la Consistencia: Creando Respuestas API Predecibles en Spring MVC

El Arte de la Consistencia: Creando Respuestas API Predecibles en Spring MVC

El Arte de la Consistencia: Creando Respuestas API Predecibles en Spring MVC Imagina a un desarrollador frontend consumiendo tu API. En un endpoint, recibe un objeto JSON. En otro, una simple lista. S

Leer más