☕ Colecciones en Java

Aprende a manejar múltiples datos de forma sencilla

Introducción a Colecciones

Imagina que eres el gerente de una empresa y necesitas guardar información de muchos empleados.

¿Usarías una variable para cada empleado?

String empleado1 = "Juan";
String empleado2 = "María";
String empleado3 = "Carlos";
// ❌ Esto no escala... ¿Y si hay 100 empleados?

🤔 El Problema

Sin colecciones, tu código sería:

  • ❌ Imposible de mantener (1000+ líneas para 1000 datos)
  • ❌ Frágil (un pequeño cambio lo rompe todo)
  • ❌ No escalable (doble trabajo para el doble de datos)
  • ❌ Difícil de entender (caótico)

¡Aquí aparecen las colecciones! Son como contenedores que permiten guardar muchos datos en una sola estructura. Java ofrece varias opciones según lo que necesites:

Tipo Uso Ejemplo Mental
Array Tamaño fijo Una bandera con 5 posiciones fijas
List Flexible, ordenado Una lista editable en tu teléfono
Set Sin duplicados Una lista asistencia (cada persona una sola vez)
Map Clave → Valor Una agenda de teléfono (nombre → número)
Stream Procesar datos Un filtro que procesa datos mientras pasan

✅ La Solución

Con colecciones: Guardar 1000 datos es TAN FÁCIL como guardar 1:

  • ✅ Una sola estructura para todos ellos
  • ✅ Código limpio y mantenible
  • ✅ Fácil de entender
  • ✅ Escalable (más datos = mismo código)

🎯 El Gran Principio

Cada colección existe por una razón. La clave es elegir la correcta para el problema que tienes.

Pregunta clave: Antes de elegir una colección, siempre pregúntate:

  • ¿Conozco el tamaño exacto?
  • ¿Permite duplicados?
  • ¿Es una relación clave-valor?
  • ¿Solo necesito procesar, no guardar?

✨ Ejemplos Prácticos

📌 Array - Horario de atención (fijo)

Caso: Una tienda abre de lunes a viernes. Siempre 5 días (tamaño FIJO).

String[] diasAbiertos = {"Lunes", "Martes", "Miércoles", "Jueves", "Viernes"};
// El tamaño NUNCA cambiará
System.out.println(diasAbiertos[0]); // Lunes

📌 List - Compras en el supermercado (flexible)

Caso: Carrito de compras. Empiezas sin nada, agregas productos, eliminas algunos.

List<String> carrito = new ArrayList<>();
carrito.add("Leche");
carrito.add("Pan");
carrito.add("Huevos");
carrito.remove("Pan"); // Cambié de opinión
// La lista creció y se encogió dinámicamente

📌 Set - Usuarios registrados (sin duplicados)

Caso: Sistema de registro. Un usuario no puede estar dos veces.

Set<String> usuarios = new HashSet<>();
usuarios.add("juan@mail.com");
usuarios.add("ana@mail.com");
usuarios.add("juan@mail.com"); // Rechazado (duplicado)
System.out.println(usuarios.size()); // 2, no 3

📌 Map - Precios de productos (relación clave-valor)

Caso: Tienda. Cada producto tiene un precio único.

Map<String, Double> precios = new HashMap<>();
precios.put("Leche", 2.50);
precios.put("Pan", 1.20);
precios.put("Huevos", 3.00);
System.out.println(precios.get("Leche")); // 2.50

📌 Stream - Filtrar y procesar (transformation)

Caso: Tienes 100 productos, solo quieres los que cuestan menos de $3.

Map<String, Double> precios = ...; // 100 productos
precios.entrySet().stream()
  .filter(p → p.getValue() < 3.0)
  .forEach(p → System.out.println(p.getKey()));
// Solo muestra: Pan, Leche

1. Arrays (Arreglos)

¿Qué es un Array?

Un array es una colección con tamaño FIJO. Una vez creado, no puedes agregar ni quitar elementos.

Analogía 1: Un libreto de teatro con exactamente 5 actos. No puedes agregar un 6to sin reescribir todo.

Analogía 2: Una fila de asientos en un cine. Decides que la fila tendrá 10 asientos y no puedo cambiar eso después.

Analogía 3: Una caja de huevos de 12 espacios. Si quieres 15 huevos, necesitas otra caja, no puedes expandir la actual.

💡 ¿Por qué existen los Arrays?

Los arrays son la forma más básica y rápida de almacenar múltiples datos. Son muy eficientes en memoria y acceso rápido. Pero la desventaja es que NO son flexibles.

Ejemplo: Si eres profesor y sabes que tu clase tiene exactamente 30 estudiantes, un array es perfecto. Pero si es una clase abierta donde vienen y van personas, no lo es.

Características clave:

📏 Tamaño fijo - Defines el tamaño al crear y no cambia
📍 Índices - Los elementos se identifican por posición (empezando en 0)
Rápido - Acceso directo por índice

Cómo se usa:

// Declarar un array de 3 números
int[] edades = new int[3];

// Asignar valores
edades[0] = 20; // Primera posición
edades[1] = 25; // Segunda posición
edades[2] = 30; // Tercera posición

// O declarar con valores directos
int[] numeros = {10, 20, 30};

// Acceder
System.out.println(edades[0]); // Imprime: 20

Recorrer un Array:

// Con for tradicional
for (int i = 0; i < edades.length; i++) {
  System.out.println(edades[i]);
}

// Con foreach (más simple)
for (int edad : edades) {
  System.out.println(edad);
}

✅ Úsalo cuando:

  • Sabes exactamente cuántos elementos habrá
  • El tamaño NO va a cambiar
  • Estás comenzando a aprender
  • Ejemplo: 7 días de la semana, 12 meses

❌ NO lo uses cuando:

  • No sabes cuántos elementos habrá
  • Necesitas agregar o eliminar elementos
  • El tamaño cambia constantemente

Ejercicios - Arrays

1️⃣ Crea un array de 5 números y suma todos

int[] nums = {10, 20, 30, 40, 50};
int suma = 0;
for (int n : nums) {
  suma += n;
}
System.out.println("Suma: " + suma); // 150

2️⃣ Crea un array de nombres y imprime todos

String[] nombres = {"Ana", "Juan", "Carlos"};
for (String nombre : nombres) {
  System.out.println(nombre);
}

3️⃣ Encuentra el número mayor en un array

int[] nums = {5, 12, 8, 3, 15};
int mayor = nums[0];
for (int i = 1; i < nums.length; i++) {
  if (nums[i] > mayor) {
    mayor = nums[i];
  }
}
System.out.println(mayor); // 15

4️⃣ Accede al último elemento sin contar manualmente

int[] datos = {1, 2, 3, 4, 5};
int ultimo = datos[datos.length - 1];
System.out.println(ultimo); // 5

5️⃣ Cuenta cuántos números pares hay

int[] nums = {10, 15, 20, 23, 30};
int pares = 0;
for (int n : nums) {
  if (n % 2 == 0) pares++;
}
System.out.println("Pares: " + pares); // 3

6️⃣ Invierte todos los elementos del array

int[] nums = {1, 2, 3, 4, 5};
for (int i = nums.length - 1; i >= 0; i--) {
  System.out.print(nums[i] + " ");
}
// Imprime: 5 4 3 2 1

7️⃣ Busca si un valor existe en el array

String[] frutas = {"Manzana", "Banana", "Naranja"};
String buscar = "Banana";
boolean existe = false;
for (String fruta : frutas) {
  if (fruta.equals(buscar)) {
    existe = true;
    break;
  }
}
System.out.println(existe); // true

8️⃣ Calcula el promedio de una array de notas

double[] notas = {7.5, 8.0, 6.5, 9.0};
double suma = 0;
for (double nota : notas) {
  suma += nota;
}
double promedio = suma / notas.length;
System.out.println("Promedio: " + promedio); // 7.75

9️⃣ Crea un array y duplica todos sus valores

int[] nums = {5, 10, 15};
for (int i = 0; i < nums.length; i++) {
  nums[i] = nums[i] * 2;
}
for (int n : nums) System.out.print(n + " ");
// Imprime: 10 20 30

🔟 Encuentra el elemento mínimo del array

int[] valores = {25, 12, 8, 30, 15};
int minimo = valores[0];
for (int i = 1; i < valores.length; i++) {
  if (valores[i] < minimo) {
    minimo = valores[i];
  }
}
System.out.println(minimo); // 8

1️⃣1️⃣ Crea una matriz 2D (array de arrays)

int[][] matriz = {{1, 2, 3}, {4, 5, 6}};
for (int[] fila : matriz) {
  for (int valor : fila) {
    System.out.print(valor + " ");
  }
  System.out.println();
}

1️⃣2️⃣ Copia los elementos de un array a otro

int[] original = {1, 2, 3, 4, 5};
int[] copia = new int[original.length];
for (int i = 0; i < original.length; i++) {
  copia[i] = original[i];
}
// O más fácil: int[] copia = original.clone();

2. List / ArrayList

¿Qué es una List?

Una List es como un Array, pero con tamaño FLEXIBLE. Puedes agregar y eliminar elementos cuando quieras.

Analogía 1: Una lista de tareas en tu teléfono. Puedes agregar tareas, eliminar completadas, sin límites.

Analogía 2: A diferencia de un array que es como una caja de huevos cerrada, una List es como una bolsa elástica que crece cuando agregamos cosas y se encoge cuando sacamos.

Analogía 3: Un carrito de compras. Empiezas sin nada, vas agregando productos según los encuentras, y puedes eliminar cualquiera que cambies de opinión.

💡 Diferencia clave: Array vs List

Array: Tienes que saber cuántos elementos EXACTAMENTE habrá. Es rígido.

List: No sabes cuántos elementos habrá. Empieza vacía y crece según necesites.

Regla de oro: Si el tamaño puede cambiar → USA LIST. Si es fijo → USA ARRAY.

Características clave:

📈 Tamaño dinámico - Crece o encoge según necesites
Flexible - Agregar y eliminar es fácil (add, remove)
📍 Ordenado - Mantiene el orden de inserción

Cómo se usa:

// Crear una List
List<String> nombres = new ArrayList<>();

// Agregar elementos
nombres.add("Ana");
nombres.add("Juan");
nombres.add("Carlos");

// Acceder
System.out.println(nombres.get(0)); // Ana

// Modificar
nombres.set(1, "María");

// Eliminar
nombres.remove(0); // Elimina "Ana"

// Tamaño
System.out.println(nombres.size()); // 2

Recorrer una List:

// Con foreach (recomendado)
for (String nombre : nombres) {
  System.out.println(nombre);
}

// Con for tradicional
for (int i = 0; i < nombres.size(); i++) {
  System.out.println(nombres.get(i));
}

✅ Úsalo cuando:

  • No sabes cuántos elementos habrá
  • Necesitas agregar o eliminar elementos
  • El tamaño cambia constantemente
  • Ejemplo: carrito de compras, lista de tareas

Ejercicios - List

1️⃣ Crea una lista de números y suma todos

List<Integer> numeros = new ArrayList<>();
numeros.add(10);
numeros.add(20);
numeros.add(30);
int suma = 0;
for (int num : numeros) suma += num;
System.out.println(suma); // 60

2️⃣ Agrega 5 nombres y muéstralos en orden inverso

List<String> nombres = new ArrayList<>();
nombres.add("Ana"); nombres.add("Juan"); nombres.add("Carlos");
for (int i = nombres.size()-1; i >= 0; i--) {
  System.out.println(nombres.get(i));
}

3️⃣ Elimina un elemento específico de la lista

List<String> frutas = new ArrayList<>();
frutas.add("Manzana");
frutas.add("Banana");
frutas.add("Naranja");
frutas.remove("Banana"); // remove por valor
// O: frutas.remove(1); // remove por índice
System.out.println(frutas); // [Manzana, Naranja]

4️⃣ Encuentra el elemento mayor en una List de números

List<Integer> numeros = new ArrayList<>();
numeros.add(5); numeros.add(15); numeros.add(10);
int mayor = numeros.get(0);
for (int num : numeros) {
  if (num > mayor) mayor = num;
}
System.out.println(mayor); // 15

5️⃣ Verifica si una List contiene un elemento

List<String> marcas = new ArrayList<>();
marcas.add("Nike");
marcas.add("Adidas");
if (marcas.contains("Nike")) {
  System.out.println("¡Encontrada!");
}

6️⃣ Reemplaza un elemento en la lista

List<String> seleccion = new ArrayList<>();
seleccion.add("Python");
seleccion.add("Java");
seleccion.set(0, "JavaScript"); // Reemplaza
System.out.println(seleccion); // [JavaScript, Java]

7️⃣ Crea una lista de nombres y cuenta su longitud

List<String> estudiantes = new ArrayList<>();
estudiantes.add("Pedro");
estudiantes.add("Laura");
estudiantes.add("Diego");
System.out.println("Total: " + estudiantes.size()); // 3

8️⃣ Filtra una lista (solo números > 10)

List<Integer> todos = new ArrayList<>();
todos.add(5); todos.add(15); todos.add(8); todos.add(20);
List<Integer> mayores = new ArrayList<>();
for (int num : todos) {
  if (num > 10) mayores.add(num);
}
System.out.println(mayores); // [15, 20]

9️⃣ Vacía completamente una lista

List<Integer> datos = new ArrayList<>();
datos.add(1); datos.add(2); datos.add(3);
datos.clear();
System.out.println(datos.size()); // 0

🔟 Obtén el índice de un elemento específico

List<String> colores = new ArrayList<>();
colores.add("Rojo");
colores.add("Azul");
colores.add("Verde");
int indice = colores.indexOf("Azul");
System.out.println(indice); // 1

1️⃣1️⃣ Convierte un Array a List

String[] array = {"A", "B", "C"};
List<String> lista = new ArrayList<>(
  Arrays.asList(array)
);
System.out.println(lista); // [A, B, C]

1️⃣2️⃣ Copia una lista a otra lista

List<Integer> original = new ArrayList<>();
original.add(1); original.add(2);
List<Integer> copia = new ArrayList<>(original);
copia.add(3); // No afecta original
System.out.println(original); // [1, 2]
System.out.println(copia); // [1, 2, 3]

3. Set (Conjunto)

¿Qué es un Set?

Un Set es una colección que NO permite duplicados. Es como una lista de asistencia: cada persona cuenta solo una vez.

Analogía 1: Un conjunto de amigos únicos. No puedes tener el mismo amigo dos veces en tu lista.

Analogía 2: Una lista de asistencia en clase. "¿Juan está?" "Sí" (se marca). "¿Juan está?" "Ya fue marcado". No puedes marcar dos veces a la misma persona.

Analogía 3: Un set de Pokemon. Cada Pokémon es único. Si intentas agregar uno que ya tienes, el sistema lo rechaza.

💡 ¿Cuándo es útil?

Los Sets son perfectos para:

  • Validar duplicados: Si hago .add("Juan") dos veces, solo queda UNO
  • Búsquedas rápidas: Verificar si algo existe es muy rápido
  • Datos únicos: Correos, IDs, números de documento, etc.

Diferencia: Set te salva de duplicados AUTOMÁTICAMENTE, sin que tengas que validar manualmente.

Características clave:

🚫 Sin duplicados - Si intentas agregar uno que ya existe, se ignora
🔀 Sin orden garantizado - Los elementos NO están en orden específico
Búsqueda rápida - Verificar si existe algo es muy rápido

Cómo se usa:

// Crear un Set
Set<String> correos = new HashSet<>();

// Agregar elementos
correos.add("a@mail.com");
correos.add("b@mail.com");
correos.add("a@mail.com"); // duplicado, se ignora

// Verificar si existe
if (correos.contains("a@mail.com")) {
  System.out.println("Existe!");
}

// Eliminar
correos.remove("b@mail.com");

// Tamaño
System.out.println(correos.size()); // 1

Recorrer un Set:

// Con foreach
for (String correo : correos) {
  System.out.println(correo);
}

✅ Úsalo cuando:

  • NO deben existir duplicados
  • Solo te importa la unicidad, no el orden
  • Ejemplo: correos únicos, IDs únicos, RUTs únicos

Ejercicios - Set

1️⃣ Crea un Set de números y agrega duplicados

Set<Integer> numeros = new HashSet<>();
numeros.add(5);
numeros.add(10);
numeros.add(5); // repetido
System.out.println(numeros.size()); // 2 (no 3!)

2️⃣ Verifica si un elemento existe en el Set

Set<String> idiomas = new HashSet<>();
idiomas.add("Python");
idiomas.add("Java");
if (idiomas.contains("Python")) {
  System.out.println("Sí existe");
}

3️⃣ Crea un Set de nombres únicos y muéstralos

Set<String> nombres = new HashSet<>();
nombres.add("Ana");
nombres.add("Juan");
nombres.add("Ana"); // ignorado
for (String nombre : nombres) {
  System.out.println(nombre);
}

4️⃣ Elimina un elemento del Set

Set<String> marcas = new HashSet<>();
marcas.add("Nike");
marcas.add("Adidas");
marcas.remove("Nike");
System.out.println(marcas.size()); // 1

5️⃣ Cuenta elementos únicos en una lista de números

int[] numeros = {1, 2, 2, 3, 3, 3, 4};
Set<Integer> unicos = new HashSet<>();
for (int num : numeros) {
  unicos.add(num);
}
System.out.println(unicos.size()); // 4 únicos

6️⃣ Crea un Set de correos y verifica duplicados

Set<String> emails = new HashSet<>();
String[] lista = {"a@x.com", "b@x.com", "a@x.com"};
for (String email : lista) {
  emails.add(email);
}
System.out.println("Únicos: " + emails.size()); // 2

7️⃣ Vacía un Set completamente

Set<Integer> datos = new HashSet<>();
datos.add(1); datos.add(2); datos.add(3);
datos.clear();
System.out.println(datos.size()); // 0

8️⃣ Convierte una List a Set para eliminar duplicados

List<Integer> conDuplicados = Arrays.asList(1,2,2,3,3,3);
Set<Integer> sinDuplicados = new HashSet<>(conDuplicados);
System.out.println(sinDuplicados.size()); // 3

9️⃣ Crea dos Sets y encuentra intersección

Set<Integer> a = new HashSet<>(Arrays.asList(1,2,3));
Set<Integer> b = new HashSet<>(Arrays.asList(2,3,4));
a.retainAll(b); // Intersección
System.out.println(a); // [2, 3]

🔟 Compara el tamaño antes y después de agregar duplicados

Set<String> palabras = new HashSet<>();
palabras.add("gato");
System.out.println("Antes: " + palabras.size()); // 1
palabras.add("gato"); // duplicado
System.out.println("Después: " + palabras.size()); // 1 (sin cambios)

1️⃣1️⃣ Crea un Set de identificadores únicos

Set<String> ids = new HashSet<>();
ids.add("ID001");
ids.add("ID002");
ids.add("ID001"); // duplicado
System.out.println("IDs únicos: " + ids.size()); // 2

1️⃣2️⃣ Usa Set para validar que todos son únicos

String[] usuarios = {"Ana", "Juan", "Carlos"};
Set<String> conjunto = new HashSet<>(Arrays.asList(usuarios));
if (conjunto.size() == usuarios.length) {
  System.out.println("Todos son únicos");
} else {
  System.out.println("Hay duplicados");
}

4. Map / HashMap

¿Qué es un Map?

Un Map almacena datos en pares: clave → valor. Es como una agenda: buscas por nombre y obtienes un teléfono.

Analogía 1: Un diccionario donde cada palabra (clave) tiene una definición (valor). Buscas "gato" y encuentras "animal doméstico".

Analogía 2: Una agenda de teléfono. La clave es el nombre ("María") y el valor es el teléfono ("555-1234").

Analogía 3: Un carnet de biblioteca. El carnet es la CLAVE (único para cada persona) y el VALOR es "la lista de libros de dicha persona".

💡 ¿Por qué NO usar List para todo?

Imagina que tienes dos Lists:

  • List con nombres: ["Juan", "Ana", "Carlos"]
  • List con sueldos: [500000, 650000, 600000]

¿Y si alguien borra a Ana? ¡Ahora los sueldos NO coinciden! 😱

Con Map: Haces: sueldos.put("Ana", 650000). ¡La relación NUNCA se rompe!

Regla: Si los datos están relacionados (usuario ↔ contraseña, producto ↔ precio), USA MAP.

Características clave:

🔑 Clave-Valor - Cada dato tiene una clave única para buscarlo
🚫 Claves únicas - No puede haber dos claves iguales
Búsqueda rápida - Encontrar un valor por clave es instantáneo

Cómo se usa:

// Crear un Map
Map<String, Integer> sueldos = new HashMap<>();

// Agregar datos (clave, valor)
sueldos.put("Juan", 500000);
sueldos.put("Ana", 650000);

// Obtener un valor por clave
int sueldo = sueldos.get("Juan");
System.out.println(sueldo); // 500000

// Verificar si una clave existe
if (sueldos.containsKey("Ana")) {
  System.out.println("Existe");
}

// Eliminar
sueldos.remove("Juan");

// Tamaño
System.out.println(sueldos.size()); // 1

Recorrer un Map:

// Usar entrySet (la forma recomendada)
for (Map.Entry<String, Integer> entry : sueldos.entrySet()) {
  String nombre = entry.getKey();
  int sueldo = entry.getValue();
  System.out.println(nombre + " → " + sueldo);
}

✅ Úsalo cuando:

  • Necesitas buscar un dato por una clave
  • Hay relaciones directas (usuario → contraseña)
  • Acceso rápido es importante
  • Ejemplo: un diccionario, una agenda, precios de productos

Ejercicios - Map

1️⃣ Crea un Map de estudiantes y sus notas

Map<String, Double> notas = new HashMap<>();
notas.put("Ana", 8.5);
notas.put("Juan", 7.0);
notas.put("Carlos", 9.0);
System.out.println(notas.get("Ana")); // 8.5

2️⃣ Verifica si una clave existe en el Map

Map<String, String> capitales = new HashMap<>();
capitales.put("Chile", "Santiago");
capitales.put("Perú", "Lima");
if (capitales.containsKey("Chile")) {
  System.out.println("Encontrada");
}

3️⃣ Recorre todas las claves del Map

Map<String, Integer> edades = new HashMap<>();
edades.put("María", 25);
edades.put("José", 30);
for (String nombre : edades.keySet()) {
  System.out.println(nombre);
}

4️⃣ Recorre todos los valores del Map

Map<String, Integer> precios = new HashMap<>();
precios.put("Manzana", 50);
precios.put("Naranja", 75);
for (int precio : precios.values()) {
  System.out.println(precio);
}

5️⃣ Suma todos los valores de un Map de números

Map<String, Integer> ventas = new HashMap<>();
ventas.put("Lunes", 100);
ventas.put("Martes", 150);
ventas.put("Miércoles", 120);
int total = 0;
for (int venta : ventas.values()) total += venta;
System.out.println("Total: " + total); // 370

6️⃣ Reemplaza un valor en el Map

Map<String, String> usuarios = new HashMap<>();
usuarios.put("juan", "Juan123");
usuarios.put("juan", "NuevaPswd456"); // Reemplaza
System.out.println(usuarios.get("juan")); // NuevaPswd456

7️⃣ Elimina un par clave-valor del Map

Map<String, Integer> datos = new HashMap<>();
datos.put("A", 1);
datos.put("B", 2);
datos.remove("A");
System.out.println(datos.size()); // 1

8️⃣ Cuenta cuántas claves tiene el Map

Map<String, Double> calificaciones = new HashMap<>();
calificaciones.put("Mate", 8.0);
calificaciones.put("Historia", 7.5);
calificaciones.put("Inglés", 9.0);
System.out.println("Total: " + calificaciones.size()); // 3

9️⃣ Crea un Map y recorre todos los pares clave-valor

Map<String, String> paises = new HashMap<>();
paises.put("MX", "México");
paises.put("AR", "Argentina");
for (Map.Entry<String, String> entry : paises.entrySet()) {
  System.out.println(entry.getKey() + " = " + entry.getValue());
}

🔟 Vacía completamente un Map

Map<Integer, String> registro = new HashMap<>();
registro.put(1, "Uno");
registro.put(2, "Dos");
registro.clear();
System.out.println(registro.size()); // 0

1️⃣1️⃣ Obtén un valor con valor por defecto si no existe

Map<String, Integer> edades = new HashMap<>();
edades.put("Ana", 25);
int edad = edades.getOrDefault("Pedro", 0);
System.out.println(edad); // 0

1️⃣2️⃣ Cuenta valores duplicados en un Map

Map<String, String> equipos = new HashMap<>();
equipos.put("Juan", "Rojo");
equipos.put("Ana", "Rojo");
equipos.put("Carlos", "Azul");
int rojos = 0;
for (String color : equipos.values()) {
  if (color.equals("Rojo")) rojos++;
}
System.out.println("Rojos: " + rojos); // 2

5. Stream API

¿Qué es un Stream?

Un Stream es una forma elegante de procesar colecciones. NO almacena datos, sino que los filtra, transforma y procesa.

Analogía 1: Un filtro de café: el agua pasa por el filtro transformándose, no se guarda.

Analogía 2: Una línea de producción en fábrica. El producto entra, se transforma en estaciones, sale un producto diferente. Pero el proceso es lo importante, no el almacenamiento.

Analogía 3: Un inspector de correo. Lee cartas (filter), puede marcar algunas (map), luego las envía. El inspector no GUARDA las cartas.

💡 Stream NO es una colección

IMPORTANTE: Un Stream NO almacena datos. Es solo un "pipeline" de procesamiento.

Comparación:

  • List: GUARDA "Juan", "Ana", "Carlos" en memoria
  • Stream: PROCESA "Juan", "Ana", "Carlos". No los guarda (a menos que captures el resultado)

Por eso es más eficiente: no creas una colección nueva, solo "filtras" la que ya existe.

Características clave:

🚫 No almacena - Solo procesa datos
📝 Declarativo - Dice QUÉ hacer, no CÓMO hacerlo
Legible - Código limpio y expresivo

Operaciones principales:

Operación Qué hace Ejemplo
filter() Filtra elementos que cumplen una condición n → n > 10
map() Transforma cada elemento n → n * 2
forEach() Itera sobre cada elemento print(n)
count() Cuenta elementos stream.count()

Cómo se usa:

List<Integer> numeros = Arrays.asList(5, 10, 15, 20, 25);

// Mostrar números > 10
numeros.stream()
  .filter(n → n > 10)
  .forEach(System.out::println);
// Imprime: 15, 20, 25

// Duplicar números y contar
long cantidad = numeros.stream()
  .map(n → n * 2)
  .count();
System.out.println(cantidad); // 5

✅ Úsalo cuando:

  • Necesitas filtrar, transformar o procesar una colección
  • Quieres código más limpio y legible
  • Necesitas composar múltiples operaciones

❌ NO lo uses cuando:

  • Solo iteras (un foreach normal es más simple)
  • Necesitas modificar la colección original

Ejercicios - Stream

1️⃣ Filtra números mayores a 10

List<Integer> nums = Arrays.asList(5,12,8,15,3);
nums.stream()
  .filter(n → n > 10)
  .forEach(System.out::println);
// Imprime: 12, 15

2️⃣ Duplica cada número y muéstralos

List<Integer> numeros = Arrays.asList(2,3,4);
numeros.stream()
  .map(n → n * 2)
  .forEach(System.out::println);
// Imprime: 4, 6, 8

3️⃣ Cuenta cuántos elementos hay en un Stream

List<String> nombres = Arrays.asList("Ana","Juan","Carlos");
long total = nombres.stream().count();
System.out.println(total); // 3

4️⃣ Filtra números pares

List<Integer> nums = Arrays.asList(1,2,3,4,5,6);
nums.stream()
  .filter(n → n % 2 == 0)
  .forEach(System.out::println);
// Imprime: 2, 4, 6

5️⃣ Convierte a mayúsculas y filtra

List<String> palabras = Arrays.asList("java","python","c++");
palabras.stream()
  .map(String::toUpperCase)
  .forEach(System.out::println);
// Imprime: JAVA, PYTHON, C++

6️⃣ Filtra y transforma en una sola línea

List<Integer> nums = Arrays.asList(1,2,3,4,5,6);
nums.stream()
  .filter(n → n > 2)
  .map(n → n * 10)
  .forEach(System.out::println);
// Imprime: 30, 40, 50, 60

7️⃣ Cuántos nombres tienen más de 4 letras

List<String> nombres = Arrays.asList("Ana","Juan","Carlos","Leo");
long count = nombres.stream()
  .filter(n → n.length() > 4)
  .count();
System.out.println(count); // 2 (Juan y Carlos)

8️⃣ Filtra nombres que empiezan con 'A'

List<String> nombres = Arrays.asList("Ana","Carlos","Andrés");
nombres.stream()
  .filter(n → n.startsWith("A"))
  .forEach(System.out::println);
// Imprime: Ana, Andrés

9️⃣ Suma todos los números usando reduce

List<Integer> nums = Arrays.asList(1,2,3,4,5);
int suma = nums.stream()
  .reduce(0, (a,b) → a + b);
System.out.println(suma); // 15

🔟 Filtra, transforma y recolecta en Lista

List<Integer> nums = Arrays.asList(1,2,3,4,5);
List<Integer> resultado = nums.stream()
  .filter(n → n > 2)
  .map(n → n * 10)
  .collect(Collectors.toList());
System.out.println(resultado); // [30,40,50]

1️⃣1️⃣ Obtén el número máximo del stream

List<Integer> nums = Arrays.asList(5,2,9,1,7);
int maximo = nums.stream()
  .max(Integer::compare)
  .orElse(0);
System.out.println(maximo); // 9

1️⃣2️⃣ Obtén el número mínimo del stream

List<Integer> nums = Arrays.asList(5,2,9,1,7);
int minimo = nums.stream()
  .min(Integer::compare)
  .orElse(0);
System.out.println(minimo); // 1

Cuándo usar cada una - Guía Rápida

Árbol de decisión:

¿Sabes el tamaño exacto?

  • SÍ → Array
  • NO → ¿Permite duplicados?

¿Permite duplicados?

  • SÍ → ¿Es clave-valor?
  • NO → Set

¿Es clave-valor?

  • SÍ → Map
  • NO → List

Ejemplos del mundo real:

📚 Sistema de biblioteca

  • List para libros disponibles (puede cambiar)
  • Set para IDs de usuarios (sin duplicados)
  • Map para usuario → historial de préstamos

🛒 Carrito de compras

  • List para productos (puede crecer)
  • Map para producto → cantidad
  • Stream para calcular total

👥 Red social

  • Set para amigos únicos (sin duplicados)
  • Map para usuario → perfil
  • List para posts (orden importa)
Necesidad Colección Razón
Datos con posición fija Array Tamaño fijo, acceso rápido
Datos que crecen List Dinámico, flexible
Sin duplicados Set Unicidad garantizada
Buscar por clave Map Acceso rápido directo
Procesar datos Stream Código limpio y expresivo

⚔️ Ejemplos Comparativos

Escenario: Registrar calificaciones de estudiantes

❌ SIN COLECCIONES (Caótico):

// Para 30 estudiantes, código gigante y propenso a errores
double nota1 = 8.5;
double nota2 = 9.0;
double nota3 = 7.5;
... 27 más
// Si necesitas promedio: 30 líneas de código

✅ CON ARRAY (Si sabemos que siempre hay 30):

double[] calificaciones = new double[30];
calificaciones[0] = 8.5;
calificaciones[1] = 9.0;
// ... llenar los 30
double suma = 0;
for (double nota : calificaciones) suma += nota;
double promedio = suma / calificaciones.length;

✅ CON LIST (Si no sabemos cuántos):

List<Double> calificaciones = new ArrayList<>();
calificaciones.add(8.5);
calificaciones.add(9.0);
// Agregar cuantos necesites
double promedio = calificaciones.stream()
  .mapToDouble(Double::doubleValue)
  .average().orElse(0);

✅ CON MAP (Si quieres nombre → nota):

Map<String, Double> calificaciones = new HashMap<>();
calificaciones.put("Juan", 8.5);
calificaciones.put("Ana", 9.0);
// Busca rápido por nombre
double notaDeJuan = calificaciones.get("Juan"); // 8.5

✅ CON SET (Solo estudiantes únicos):

Set<String> estudiantes = new HashSet<>();
estudiantes.add("Juan");
estudiantes.add("Ana");
estudiantes.add("Juan"); // Rechazado (duplicado)
System.out.println(estudiantes.size()); // 2, no 3

✅ CON STREAM (Filtrar aprobados):

Map<String, Double> notas = ...; // Todas las notas
notas.entrySet().stream()
  .filter(entry → entry.getValue() >= 6.0)
  .forEach(entry → System.out.println(
    entry.getKey() + " aprobó"));
// Muestra solo: Juan, Ana, ...

💡 Consejo de Oro

No existe colección "mejor". Existe la colección correcta para el problema que tienes. Elige basándote en:

  1. ¿Necesito tamaño fijo?
  2. ¿Puedo tener duplicados?
  3. ¿Necesito buscar rápido por clave?
  4. ¿Debo procesar datos?