Aikido

Por que você deve usar padrões seguros ao remover itens de coleções

Legibilidade

Regra
Utilização métodos métodos ao remover de coleções.
Modificar uma coleção enquanto a sobre ela frequentemente causa erros.

Idiomas suportados: PY, Java, C/C++, C#, 
Swift/Objective-C, Ruby, PHP, Kotlin, go,
Scala, Rust, Groovy, Dart, Julia, Elixit, 
Erlang, Clojure, OCaml, Lua

Introdução

Remover itens de uma coleção durante a iteração causa exceções de modificação concorrente em Java e comportamento imprevisível em C#. O iterador mantém um ponteiro interno que se torna inválido quando a coleção subjacente é alterada. Isso leva a elementos ignorados, falhas ou loops infinitos, dependendo do tipo de coleção e do padrão de remoção utilizados.

Por que isso importa

Estabilidade do sistema: Exceções de modificação concorrente travam a aplicação imediatamente. Em produção, isso significa requisições perdidas e indisponibilidade do serviço. A exceção frequentemente ocorre em casos de borda com dados específicos, tornando-a difícil de detectar durante os testes.

Integridade dos dados: Quando a lógica de remoção falha no meio de uma iteração, a coleção é deixada em um estado parcialmente modificado. Alguns itens são removidos enquanto outros que deveriam ter sido removidos permanecem. Isso cria dados inconsistentes que afetam a lógica a jusante.

Complexidade de depuração: Bugs de modificação concorrente dependem de tempo e podem se manifestar apenas com certas combinações de dados. São difíceis de reproduzir consistentemente, tornando-os complexos de depurar e corrigir de forma confiável.

Exemplos de código

❌ Não-conforme:

List<User> users = getUserList();
for (User user : users) {
    if (!user.isActive()) {
        users.remove(user); // ConcurrentModificationException
    }
}

Por que está errado: Removendo de usuários enquanto a iteração com for-loop aprimorado causa ConcurrentModificationException. O iterador detecta que a coleção foi modificada fora do iterador e lança uma exceção imediatamente. Quaisquer usuários ativos após o primeiro inativo nunca são processados.

✅ Compatível:

List<User> users = getUserList();
Iterator<User> iterator = users.iterator();
while (iterator.hasNext()) {
    User user = iterator.next();
    if (!user.isActive()) {
        iterator.remove(); // Safe removal through iterator
    }
}

Por que isso importa: Utilizando iterator.remove() remove itens com segurança durante a iteração. O iterador mantém um estado consistente e continua processando os itens restantes. Todos os usuários inativos são removidos corretamente, sem exceções.

Conclusão

Utilize iteradores remove() método para remoção segura durante a iteração. Alternativamente, use streams com filter() para criar novas coleções ou removeIf() para remoção em massa. Nunca chame a coleção de remove() diretamente durante a iteração.

FAQs

Dúvidas?

E quanto ao uso de loops for regulares com índice?

Iterar para trás com índice funciona: for (int i = list.size() - 1; i >= 0; i--). Remover itens desloca elementos subsequentes, mas a iteração para trás evita pular. No entanto, iterator.remove() ou removeIf() são mais claros e menos propensos a erros.

Posso usar Java 8+ removeIf em vez disso?

Sim, users.removeIf(user -> !user.isActive()) é a abordagem moderna preferida. É mais concisa e lida com a iteração de forma segura internamente. Use removeIf() ao remover com base em um predicado, streams para transformações e métodos de iterador quando a lógica de remoção for complexa.

Isso se aplica a todos os tipos de coleção?

Sim, ArrayList, HashSet, HashMap e a maioria das coleções lançam ConcurrentModificationException quando modificadas durante a iteração. Coleções thread-safe como ConcurrentHashMap permitem modificação, mas possuem semânticas diferentes. Sempre verifique a documentação da coleção para as regras de modificação.

E quanto a coleções C#?

C# lança InvalidOperationException quando coleções são modificadas durante a iteração. Use ToList() para criar uma cópia antes de iterar: foreach (var user in users.ToList()) e então remova do original. Ou use LINQ: users = users.Where(u => u.IsActive).ToList().

Como remover múltiplos itens de forma eficiente?

Use `removeIf()` para coleções únicas ou streams para filtragem complexa: `users = users.stream().filter(User::isActive).collect(Collectors.toList())`. Essas abordagens são otimizadas para operações em massa e mais eficientes do que remover itens um por um em um loop.

Fique seguro agora

Proteja seu código, Cloud e runtime em um único sistema centralizado.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados da varredura em 32 segundos.