CodeGym /Cursos /JAVA 25 SELF /Filtragem de elementos de coleções

Filtragem de elementos de coleções

JAVA 25 SELF
Nível 28 , Lição 0
Disponível

1. Introdução

Na programação, estamos constantemente diante da situação em que, de um grande conjunto de dados, precisamos selecionar apenas os elementos necessários — isso é a filtragem. Quer manter somente os números pares, encontrar strings que contenham a palavra "Java" ou selecionar usuários com mais de 18 anos — tudo isso são tarefas de filtragem.

Em Java, operações desse tipo aparecem a todo momento; por isso, é importante dominar com segurança as diferentes maneiras de realizá-las e entender suas particularidades.

2. Filtragem com laço

Vamos começar pelo método mais básico — o laço for comum. Essa abordagem é chamada de imperativa, porque você especifica explicitamente o que fazer e como fazer. Ela é legível e simples de entender.

Exemplo: manter apenas os 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<>(); // Criamos uma nova lista para o resultado

        for (Integer n : numbers) {
            if (n % 2 == 0) { // Verificamos a condição: número par
                evenNumbers.add(n);
            }
        }

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

Resultado:

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

É importante lembrar aqui: a coleção original (numbers) não é alterada. Formamos uma nova lista de resultados.

Exemplo: filtragem de strings por substring

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]

O mesmo princípio: percorremos a lista de strings e verificamos a presença da substring com o método contains.

3. Remover elementos de uma coleção: por que nem tudo é tão simples?

Onde está a pegadinha?

Às vezes queremos remover da coleção original todos os elementos indesejados. Porém, se tentar fazer isso durante a iteração com um laço for-each, você receberá uma exceção ConcurrentModificationException.

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

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

Resultado:

Exception in thread "main" java.util.ConcurrentModificationException

As coleções “não gostam” de ser modificadas durante esse tipo de percurso — a iteração se quebra.

Como remover elementos de uma coleção do jeito certo?

Modo 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(); // Removemos com segurança!
    }
}
System.out.println(numbers); // [1, 3, 5]

Aqui criamos um iterador com o método iterator(), percorremos a coleção por meio de hasNext() e next(), e removemos os elementos indesejados chamando it.remove().

Modo 2: criar uma nova lista apenas com os elementos necessários

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]

Não tocamos na coleção original e montamos uma nova lista. A abordagem é segura, clara e se expande facilmente quando as condições ficam mais complexas.

Modo 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]

Em uma única linha passamos a condição: “remova tudo o que for menor que zero”. Os detalhes internos da modificação segura ficam a cargo da coleção.

Em resumo: para remover elementos, não use for-each. Escolha entre Iterator.remove(), criar uma nova lista ou o conciso removeIf.

4. Quando usar cada abordagem para filtragem?

A abordagem imperativa, com o laço comum, é conveniente quando você precisa não apenas selecionar elementos, mas também já fazer algo com eles (por exemplo, imprimir ou transformar). Ela é simples e transparente.

Se o assunto é justamente a remoção da coleção original, evite remover dentro do for-each. Use Iterator.remove() para controle passo a passo ou o moderno removeIf() — a forma mais curta e expressiva.

Exemplo: há uma lista de palavras; é preciso remover todas as palavras com menos de quatro 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]

O método removeIf recebe um predicado — a regra de filtragem — e remove tudo o que o satisfaz.

5. Erros comuns ao filtrar coleções

Erro nº 1: tentar remover elementos de uma coleção dentro de um laço for-each.
Esse código levará a ConcurrentModificationException:

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

Opções corretas: use Iterator.remove() ou removeIf.

Erro nº 2: condição de filtragem mal formulada.
É preciso remover apenas os 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]

Mas, se por engano você escrever “não positivos” e usar <=, o zero também desaparecerá:

numbers.removeIf(n -> n <= 0); // Resultado: [2, 4] — zero removido por engano

Atente para a precisão das condições: uma única comparação incorreta muda completamente o resultado.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION