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

Cuándo Usar Colas de Mensajes en el Desarrollo de Software

Cuándo Usar Colas de Mensajes en el Desarrollo de Software

Las colas de mensajes son herramientas clave para construir sistemas distribuidos, escalables y tolerantes a fallos. En este artículo te comparto una guía con situaciones comunes donde su uso es altam

RabbitMQ 1: Introducción a RabbitMQ, El Corazón de la Mensajería Asíncrona

RabbitMQ 1: Introducción a RabbitMQ, El Corazón de la Mensajería Asíncrona

En el mundo del desarrollo de software moderno, especialmente con el auge de los microservicios y los sistemas distribuidos, la forma en que las diferentes partes de una aplicación se comunican es fun

RabbitMQ 3: Configuración y Gestión de Colas en RabbitMQ

RabbitMQ 3: Configuración y Gestión de Colas en RabbitMQ

Después de entender qué es RabbitMQ y cómo sus Exchanges y Bindings dirigen los mensajes, llegamos a la Cola. La cola es fundamentalmente un buffer confiable: es el lugar donde los mensajes esperan su

RabbitMQ 2: Arquitectura y Enrutamiento Avanzado en RabbitMQ

RabbitMQ 2: Arquitectura y Enrutamiento Avanzado en RabbitMQ

En nuestro primer artículo, exploramos qué es RabbitMQ, por qué es fundamental para la comunicación asíncrona en sistemas distribuidos y cuáles son sus casos de uso típicos. Lo comparamos con una "ofi

RabbitMQ 4: Robustez y Seguridad en RabbitMQ

RabbitMQ 4: Robustez y Seguridad en RabbitMQ

Hemos recorrido el camino desde la introducción a RabbitMQ y su papel en la mensajería asíncrona, pasando por su arquitectura, componentes de enrutamiento (Exchanges y Bindings), y la gestión detallad

RabbitMQ 5: Consumo de Recursos, Latencia y Monitorización de RabbitMQ

RabbitMQ 5: Consumo de Recursos, Latencia y Monitorización de RabbitMQ

Hemos explorado la teoría detrás de RabbitMQ, su arquitectura, cómo enruta mensajes y cómo podemos construir sistemas robustos y seguros. Sin embargo, para operar RabbitMQ de manera efectiva en produc

RabbitMQ 6: Alta Disponibilidad y Escalabilidad con Clustering en RabbitMQ

RabbitMQ 6: Alta Disponibilidad y Escalabilidad con Clustering en RabbitMQ

Hasta ahora, hemos hablado de cómo un nodo individual de RabbitMQ maneja mensajes, gestiona colas, y cómo monitorizar su rendimiento y seguridad. Sin embargo, para aplicaciones críticas que no pueden

Kafka 1: Introducción a Apache Kafka, fundamentos y Casos de Uso

Kafka 1: Introducción a Apache Kafka, fundamentos y Casos de Uso

En el panorama tecnológico actual, los datos son el motor que impulsa la innovación. La capacidad de procesar, reaccionar y mover grandes volúmenes de datos en tiempo real se ha convertido en una nece

Kafka 2: Arquitectura Profunda de Kafka, Topics, Particiones y Brokers

Kafka 2: Arquitectura Profunda de Kafka, Topics, Particiones y Brokers

En nuestro primer artículo, despegamos en el mundo de Apache Kafka, sentando las bases de lo que es esta potente plataforma de streaming de eventos y diferenciándola de los sistemas de mensajería trad

Kafka 3: Productores y Consumidores, Configuración y Buenas Prácticas

Kafka 3: Productores y Consumidores, Configuración y Buenas Prácticas

Hemos navegado por los conceptos esenciales de Apache Kafka y desentrañado la arquitectura que reside bajo la superficie, comprendiendo cómo los Topics se dividen en Particiones distribuidas entre Bro

Kafka 4: Procesamiento de Datos en Tiempo Real con Kafka Streams y ksqlDB

Kafka 4: Procesamiento de Datos en Tiempo Real con Kafka Streams y ksqlDB

En los artículos anteriores, hemos construido una sólida comprensión de Apache Kafka: qué es, por qué es una plataforma líder para streaming de eventos, cómo está estructurado internamente con Topic

Spring WebFlux 1: Fundamentos Reactivos y el Corazón de Reactor

Spring WebFlux 1: Fundamentos Reactivos y el Corazón de Reactor

¡Hola, entusiasta del desarrollo moderno! 👋 En el vertiginoso mundo de las aplicaciones web, donde la escalabilidad y la eficiencia son reyes, ha surgido un paradigma que desafía el modelo tradicion

Spring WebFlux 2: Alta Concurrencia sin Más Hilos

Spring WebFlux 2: Alta Concurrencia sin Más Hilos

¡Bienvenido de nuevo a nuestra inmersión en Spring WebFlux! 👋 En la primera parte de esta serie, exploramos el "por qué" de la programación reactiva, entendiendo los problemas del bloqueo y descubri

Kafka 6: Despliegue, Seguridad y Optimización

Kafka 6: Despliegue, Seguridad y Optimización

Hemos explorado la arquitectura fundamental de Apache Kafka, la dinámica entre productores y consumidores, sus potentes capacidades para el procesamiento de flujos de datos y las herramientas que enri

Spring WebFlux 3: Comunicación, Datos y Errores Reactivos

Spring WebFlux 3: Comunicación, Datos y Errores Reactivos

¡Continuemos nuestro viaje por el fascinante mundo de Spring WebFlux! En la Parte 1, sentamos las bases de la programación reactiva y exploramos Project Reactor, el corazón de WebFlux. En la **Pa

Kafka 7: Patrones Avanzados y Anti-Patrones con Kafka

Kafka 7: Patrones Avanzados y Anti-Patrones con Kafka

Hemos recorrido un camino considerable en nuestra serie sobre Apache Kafka. Desde sus fundamentos y arquitectura interna hasta la interacción con productores y consumidores, las herramientas de proces

Kafka 5: Más Allá del Core, Explorando el Ecosistema de Apache Kafka

Kafka 5: Más Allá del Core, Explorando el Ecosistema de Apache Kafka

Hemos navegado por las entrañas de Apache Kafka, comprendiendo su funcionamiento interno, la interacción entre productores y consumidores, e incluso cómo procesar datos en tiempo real con Kafka Stream

Spring WebFlux 4: Comunicación Avanzada, Pruebas y Producción

Spring WebFlux 4: Comunicación Avanzada, Pruebas y Producción

La serie Spring WebFlux nos ha llevado a través de un viaje fascinante por el mundo de la programación reactiva, desde sus fundamentos y el poder de Project Reactor hasta la construcción de arquit

Arquitectura DDD y Hexagonal: Construyendo Software para el Futuro

Arquitectura DDD y Hexagonal: Construyendo Software para el Futuro

En el dinámico mundo del desarrollo de software, la complejidad es el enemigo silencioso. Las aplicaciones crecen, los requisitos cambian y, sin una guía clara, el código puede convertirse rápidamente

Arquitectura distribuida y el abandono consciente de ACID

Arquitectura distribuida y el abandono consciente de ACID

En el mundo de los sistemas distribuidos, hay una verdad incómoda que enfrentamos tarde o temprano: no podemos tenerlo todo. La promesa de las transacciones ACID tradicionales —esa garantía tranquiliz

Arquitectura de Base de Datos para Identidad, Autenticación y Autorización (IAM)

Arquitectura de Base de Datos para Identidad, Autenticación y Autorización (IAM)

Cuando se habla de seguridad en el contexto de una aplicación, la conversación casi siempre gira en torno a las capas visibles: el cifrado en tránsito, las políticas de contraseñas, los tokens de aute

Estándar de Arquitectura: Transacciones Distribuidas (Patrón Saga)

Estándar de Arquitectura: Transacciones Distribuidas (Patrón Saga)

PARTE I: PRINCIPIOS Y NORMATIVA 1. Fundamentos de Consistencia Eventual Debido a la naturaleza distribuida del sistema, se abandona el modelo ACID tradicional (Atomicidad inmediata con bloque

Observabilidad sin Ruido: Diseñando un Sistema de Logs con AOP en Arquitecturas DDD

Observabilidad sin Ruido: Diseñando un Sistema de Logs con AOP en Arquitecturas DDD

Hay una tensión que todo equipo de desarrollo enfrenta tarde o temprano: la necesidad de saber qué está pasando dentro del sistema sin que esa necesidad contamine el código que lo hace funcionar. Los

Arquitectura Modular por Contexto: Cuando la Teoría se Encuentra con la Realidad

Arquitectura Modular por Contexto: Cuando la Teoría se Encuentra con la Realidad

Has estado ahí. Es lunes por la mañana, abres el proyecto en tu IDE, y necesitas modificar cómo se procesa un pedido. Treinta minutos después, todavía estás navegando entre carpetas intentando encontr

Observabilidad sin Ruido: Diseñando un Sistema de Logs con AOP en Arquitecturas DDD — Parte II

Observabilidad sin Ruido: Diseñando un Sistema de Logs con AOP en Arquitecturas DDD — Parte II

La primera parte de este artículo construyó el argumento conceptual: por qué los logs dispersos se convierten en deuda técnica, cómo AOP permite centralizar la observabilidad sin contaminar la lógica

Cuando un sistema debe ejecutar lo mismo siempre y algo distinto cada vez

Cuando un sistema debe ejecutar lo mismo siempre y algo distinto cada vez

Imagina que estás diseñando el flujo de solicitud de productos financieros de un banco. Un cliente puede pedir una tarjeta de crédito o un crédito para comprar un vehículo. Los dos productos son disti

Cuando el sistema nuevo tiene que hablarle al sistema viejo en su idioma

Cuando el sistema nuevo tiene que hablarle al sistema viejo en su idioma

Imagina que llevas meses construyendo un sistema moderno sobre PostgreSQL, desplegado en contenedores sobre una infraestructura en la nube. Los datos están bien estructurados, las relaciones son clara

Observabilidad sin Ruido: Diseñando un Sistema de Logs con AOP en Arquitecturas DDD — Parte III

Observabilidad sin Ruido: Diseñando un Sistema de Logs con AOP en Arquitecturas DDD — Parte III

Las dos primeras partes de esta serie resolvieron un problema bien delimitado: construir un sistema de logging centralizado que operara de forma transversal sobre una arquitectura DDD sin contaminar l

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

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

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

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

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

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

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

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

Transacciones Declarativas en Arquitecturas Hexagonales con Spring WebFlux y AOP

Transacciones Declarativas en Arquitecturas Hexagonales con Spring WebFlux y AOP

En el desarrollo de aplicaciones reactivas modernas, especialmente bajo paradigmas como la Arquitectura Hexagonal, surgen desafíos que nos obligan a repensar cómo aplicamos conceptos transversales. Un

Más Allá del `try-catch`: Diseñando un Sistema de Excepciones Robusto y Escalable 🎯

Más Allá del `try-catch`: Diseñando un Sistema de Excepciones Robusto y Escalable 🎯

En el desarrollo de APIs, el manejo de errores suele ser un ciudadano de segunda clase. Con frecuencia, nos conformamos con respuestas de error genéricas, inconsistentes o, en el peor de los casos, co

Respuestas API Consistentes: Un Wrapper Transversal con Spring WebFlux y `WebFilter`

Respuestas API Consistentes: Un Wrapper Transversal con Spring WebFlux y `WebFilter`

En entornos modernos de microservicios, la consistencia en las respuestas de una API es más que una cuestión de estética: es un factor crítico para la mantenibilidad, observabilidad y experiencia del

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

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