CodeGym /Cursos /JAVA 25 SELF /Filtrado de elementos de colecciones

Filtrado de elementos de colecciones

JAVA 25 SELF
Nivel 28 , Lección 0
Disponible

1. Introducción

En programación nos encontramos constantemente con situaciones en las que, de un conjunto grande de datos, hay que seleccionar solo los elementos necesarios — eso es el filtrado. ¿Quieres dejar solo los números pares, encontrar cadenas que contengan la palabra «Java», o seleccionar usuarios mayores de 18 años? — todo eso son tareas de filtrado.

En Java operaciones similares aparecen a cada paso, por lo que es importante dominar con seguridad las distintas maneras de realizarlas y entender sus particularidades.

2. Filtrado mediante un bucle

Empecemos por la forma más básica — el bucle for habitual. A este enfoque se le llama imperativo, porque indicas explícitamente qué hacer y cómo hacerlo. Es legible y fácil de entender.

Ejemplo: dejar solo los números pares

import java.util.*;

public class FilterExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        List<Integer> evenNumbers = new ArrayList<>(); // Creamos una nueva lista para el resultado

        for (Integer n : numbers) {
            if (n % 2 == 0) { // Comprobamos la condición: número par
                evenNumbers.add(n);
            }
        }

        System.out.println("Números pares: " + evenNumbers);
    }
}

Resultado:

Números pares: [2, 4, 6, 8, 10]

Aquí es importante recordar: la colección original (numbers) no cambia. Formamos una nueva lista como resultado.

Ejemplo: filtrar cadenas por una subcadena

List<String> words = Arrays.asList("java", "python", "javascript", "kotlin", "c++");
List<String> javaWords = new ArrayList<>();

for (String word : words) {
    if (word.contains("java")) {
        javaWords.add(word);
    }
}
System.out.println(javaWords); // [java, javascript]

El mismo principio: recorremos la lista de cadenas y comprobamos la presencia de la subcadena mediante el método contains.

3. Eliminación de elementos de una colección: ¿por qué no es tan sencillo?

¿Dónde está la trampa?

A veces queremos eliminar de la colección original todos los elementos innecesarios. Pero si intentas hacerlo durante la iteración con un bucle for-each, obtendrás la excepción ConcurrentModificationException.

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));

for (Integer n : numbers) {
    if (n < 0) {
        numbers.remove(n); // ¡PELIGRO! ConcurrentModificationException!
    }
}

Resultado:

Exception in thread "main" java.util.ConcurrentModificationException

A las colecciones «no les gusta» que las modifiquen durante este tipo de recorrido — la iteración se rompe.

¿Cómo eliminar elementos de una colección correctamente?

Método 1: usar Iterator.remove()

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));

Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    Integer n = it.next();
    if (n < 0) {
        it.remove(); // ¡Eliminamos de forma segura!
    }
}
System.out.println(numbers); // [1, 3, 5]

Aquí creamos un iterador con el método iterator(), avanzamos por la colección con hasNext() y next(), y eliminamos los elementos no deseados llamando a it.remove().

Método 2: crear una lista nueva solo con los elementos necesarios

List<Integer> numbers = Arrays.asList(1, -2, 3, -4, 5);
List<Integer> positive = new ArrayList<>();

for (Integer n : numbers) {
    if (n >= 0) {
        positive.add(n);
    }
}
System.out.println(positive); // [1, 3, 5]

No tocamos la colección original y construimos una lista nueva. Es un enfoque seguro, claro y que se amplía fácilmente cuando las condiciones se vuelven más complejas.

Método 3: usar removeIf (Java 8+)

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));
numbers.removeIf(n -> n < 0);
System.out.println(numbers); // [1, 3, 5]

En una sola línea pasamos la condición: «elimina todo lo que sea menor que cero». Los detalles internos de la modificación segura los gestiona la propia colección.

En resumen: para eliminar elementos no uses for-each. Elige entre Iterator.remove(), crear una lista nueva o el conciso removeIf.

4. ¿Cuándo usar cada método para filtrar?

El enfoque imperativo mediante un bucle normal es útil cuando necesitas no solo seleccionar elementos, sino también hacer algo con ellos inmediatamente (por ejemplo, mostrarlos o transformarlos). Es simple y transparente.

Si se trata específicamente de la eliminación de la colección original, evita eliminar en for-each. Usa Iterator.remove() para un control paso a paso o el moderno removeIf(), la forma más corta y expresiva.

Ejemplo: hay una lista de palabras; hay que eliminar todas las palabras de menos de cuatro caracteres:

List<String> words = new ArrayList<>(Arrays.asList("Java", "is", "fun", "awesome", "code"));
words.removeIf(word -> word.length() < 4);
System.out.println(words); // [Java, awesome, code]

El método removeIf acepta un predicado — la regla de filtrado — y elimina todo lo que lo cumpla.

5. Errores típicos al filtrar colecciones

Error n.º 1: intentar eliminar elementos de una colección en un bucle for-each.
Ese código provocará ConcurrentModificationException:

for (Integer n : numbers) {
    if (n < 0) {
        numbers.remove(n); // ¡BOOM! ConcurrentModificationException
    }
}

Opciones correctas: usa Iterator.remove() o removeIf.

Error n.º 2: condición de filtrado mal formulada.
Hay que eliminar solo los números negativos:

List<Integer> numbers = new ArrayList<>(Arrays.asList(-3, -1, 0, 2, 4));
numbers.removeIf(n -> n < 0);
System.out.println(numbers); // [0, 2, 4]

Pero si por error escribes «no positivos» y utilizas <=, desaparecerá también el cero:

numbers.removeIf(n -> n <= 0); // Resultado: [2, 4] — el cero se eliminó por error

Presta atención a la precisión de las condiciones: una sola comparación incorrecta cambia por completo el resultado.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION