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.
GO TO FULL VERSION