Remoção de elementos de uma coleção em Java: como evitar ConcurrentModificationException

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

«Quero remover um elemento da lista em um loop — e recebo ConcurrentModificationException». Familiar?

\n

Se você já escreveu em Java e tentou remover um elemento de um ArrayList ou HashMap durante a iteração (por exemplo, em um loop for-each), com certeza já viu essa terrível exceção vermelha. Ela aparece no momento mais inoportuno, como se o Java dissesse: «Ei, o que você está fazendo? Não ouse tocar na coleção enquanto estou percorrendo-a!».

\n\n

No Stack Overflow, essa pergunta acumulou milhares de visualizações. E não é por acaso: o problema parece simples, mas por baixo dos panos esconde-se uma camada de otimizações e nuances de concorrência. Neste artigo, vamos analisar como remover objetos de uma coleção em um loop Java corretamente, sem causar ConcurrentModificationException, e sem perder desempenho.

\n\n

Vamos nessa!

\n\n

Por que ocorre ConcurrentModificationException?

\n

Resposta curta: iteradores fail-fast. A maioria das coleções do Java Collections Framework (ArrayList, HashSet, HashMap, etc.) usa um contador interno de modificações (modCount). Quando você cria um iterador, ele armazena o valor atual de modCount. A cada chamada de next(), o iterador verifica se a coleção foi modificada externamente (através dos métodos add() ou remove() da própria coleção). Se o contador divergir, a exceção é lançada.

\n\n

Exemplo de código que certamente falhará:

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nfor (String item : list) {\n    if ("B".equals(item)) {\n        list.remove(item); // ConcurrentModificationException!\n    }\n}\n
\n

Por quê? Porque o for-each usa implicitamente um iterador, e list.remove() altera o modCount diretamente, ignorando o iterador. O iterador não perdoa isso.

\n\n

Método nº1: Usar Iterator.remove() — o clássico

\n

A maneira mais simples e correta é pegar o iterador explicitamente e chamar seu método remove(). Este método sincroniza o contador interno do iterador com a coleção.

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nIterator<String> iterator = list.iterator();\nwhile (iterator.hasNext()) {\n    String item = iterator.next();\n    if ("B".equals(item)) {\n        iterator.remove(); // Seguro!\n    }\n}\nSystem.out.println(list); // [A, C, D]\n
\n

Vantagens: seguro, funciona para todas as coleções que suportam iteradores. Desvantagens: um pouco verboso. Para filtragens simples, é possível usar abordagens mais modernas.

\n\n

Método nº2: Collection.removeIf() — moderno e elegante

\n

A partir do Java 8, a maioria das coleções ganhou o método removeIf(Predicate filter). Esta é a maneira mais concisa de remover elementos por condição.

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nlist.removeIf(item -> "B".equals(item));\nSystem.out.println(list); // [A, C, D]\n
\n

Internamente, removeIf usa um iterador (ou uma passagem direta com preservação de índices), então não haverá ConcurrentModificationException. Esta é a maneira recomendada para casos simples.

\n

Desempenho: Para ArrayList, o método removeIf funciona em O(n), mas de forma eficiente — ele desloca os elementos em uma única passagem, em vez de remover um por um (o que seria O(n²)).

\n\n

Método nº3: Percorrer ao contrário pelo índice (para List)

\n

Se por algum motivo você não puder usar um iterador (por exemplo, precisar modificar vários elementos simultaneamente ou estiver trabalhando com código antigo), pode percorrer a lista em ordem inversa usando um for comum com índice.

\n
List<String> list = new A                

Blogs

Book Recommendations