0Pricing
Java Academy · Aula

Iterators Fail-Fast versus Fail-Safe

Compreenda ConcurrentModificationException, o comportamento fail-fast e quando usar iterators baseados em cópias instantâneas.

Iterators Fail-Fast versus Fail-Safe é uma aula grátis de Java Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Java Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Java Academy inclui 4 aulas no total.

Iteradores de falha rápida e de falha segura

Os iteradores de Java são classificados pelo comportamento quando a coleção subjacente é modificada durante a iteração: iteradores de falha rápida lançam uma exceção imediatamente; iteradores de falha segura continuam usando um instantâneo.

Iteradores de falha rápida

A maioria das coleções padrão de Java (ArrayList, HashMap, TreeSet) usa iteradores de falha rápida. Eles controlam um modCount, um contador de mutações. Qualquer modificação estrutural durante a iteração dispara ConcurrentModificationException.

import java.util.*;

List<String> list = new ArrayList<>(List.of("a", "b", "c"));

try {
    for (String s : list) {
        list.add("x"); // structural modification — throws!
    }
} catch (ConcurrentModificationException e) {
    System.out.println("ConcurrentModificationException caught!");
}
// Same for HashMap, TreeMap, HashSet, etc.

Por que existe a falha rápida

O comportamento de falha rápida ajuda na depuração: ele revela os erros imediatamente, em vez de permitir que a iteração continue com um estado corrompido. A alternativa, uma corrupção silenciosa, é muito mais difícil de depurar.

// modCount is incremented on every structural change
// (add, remove, clear on ArrayList)
// Iterator records modCount on creation
// On each next(), Iterator checks: if current modCount != expected, throw CME

// This detects bugs like:
List<Integer> nums = new ArrayList<>(List.of(1,2,3,4,5));
for (Integer n : nums) {
    if (n == 3) nums.remove(n); // bug caught immediately
}
// ConcurrentModificationException — not silent wrong results

Remoção segura: Iterator.remove()

A única modificação segura durante uma iteração baseada em Iterator é Iterator.remove(): ela remove o último elemento retornado e atualiza modCount.

List<Integer> nums = new ArrayList<>(List.of(1,2,3,4,5,6));
Iterator<Integer> it = nums.iterator();

while (it.hasNext()) {
    int n = it.next();
    if (n % 2 == 0) it.remove(); // safe: updates modCount
}

System.out.println(nums); // [1, 3, 5]

removeIf: alternativa moderna

Java 8 adicionou removeIf(Predicate) a Collection: uma maneira mais simples de remover elementos que correspondem a uma condição, sem gerenciar o iterador manualmente.

List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Ann", "Charlie"));

// Modern: removeIf handles iteration internally
names.removeIf(name -> name.startsWith("A"));
System.out.println(names); // [Bob, Charlie]

// Equivalent but verbose iterator approach:
Iterator<String> it = names.iterator();
while (it.hasNext()) {
    if (it.next().startsWith("A")) it.remove();
}

Iteradores de falha segura: CopyOnWriteArrayList

CopyOnWriteArrayList usa um iterador de falha segura. Ele percorre um instantâneo criado no momento em que o iterador é criado; as modificações não afetam a iteração em andamento.

import java.util.concurrent.CopyOnWriteArrayList;

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.addAll(List.of("a", "b", "c"));

for (String s : list) {
    list.add("x"); // no ConcurrentModificationException!
    System.out.print(s + " "); // prints a, b, c (snapshot)
}
System.out.println();
System.out.println(list); // [a, b, c, x, x, x] — modified copy

Iterador de ConcurrentHashMap

ConcurrentHashMap usa iteradores fracamente consistentes: eles refletem o estado em algum momento durante ou depois da criação do iterador, não lançam CME e podem ou não observar atualizações concorrentes.

import java.util.concurrent.ConcurrentHashMap;

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1); map.put("b", 2); map.put("c", 3);

for (Map.Entry<String, Integer> e : map.entrySet()) {
    map.put("d", 4); // no CME — weakly consistent
    System.out.println(e.getKey() + "=" + e.getValue());
}
// May or may not print "d=4" — weakly consistent

Collections.synchronizedList

Collections.synchronizedList NÃO é de falha segura: ela ainda usa iteradores de falha rápida, mas bloqueia o acesso concorrente. É necessário sincronizar manualmente durante a iteração.

import java.util.*;

List<String> syncList = Collections.synchronizedList(new ArrayList<>());
syncList.addAll(List.of("a", "b", "c"));

// Must synchronize during iteration!
synchronized (syncList) {
    for (String s : syncList) {
        System.out.println(s);
    }
}
// Without the synchronized block, CME is still possible from another thread

Padrão de iterador de instantâneo

Crie manualmente um instantâneo para percorrer com segurança uma coleção mutável sem sincronização.

import java.util.*;

List<String> original = new ArrayList<>(List.of("a", "b", "c"));

// Take a snapshot copy before iterating
List<String> snapshot = List.copyOf(original);

for (String s : snapshot) {
    // Safe to modify original during snapshot iteration
    original.remove(s);
    System.out.println("Removed: " + s);
}
System.out.println(original); // []

Compromissos de CopyOnWrite

CopyOnWriteArrayList é de falha segura, mas tem um custo alto para gravações. Cada mutação cria uma cópia completa.

  • Bom para: muitas leituras e gravações raras, como listas de ouvintes de eventos
  • Ruim para: mutações frequentes e coleções grandes

Resumo: falha rápida e falha segura

Resumo da comparação:

  • Falha rápida: ArrayList, HashMap, TreeMap — lançam CME quando há modificação e verificam modCount
  • Falha segura/fracamente consistente: ConcurrentHashMap, CopyOnWriteArrayList — não lançam CME e percorrem um instantâneo ou dados concorrentes
  • Métodos de modificação segura: Iterator.remove(), removeIf(), replaceAll()

Prática: limpeza de sessões expiradas

Remova com segurança sessões expiradas de uma lista concorrente usando padrões apropriados.

import java.util.*;
import java.util.concurrent.*;

class SessionManager {
    private final CopyOnWriteArrayList<Session> sessions = new CopyOnWriteArrayList<>();

    record Session(String id, long expiresAt) {
        boolean isExpired() { return System.currentTimeMillis() > expiresAt; }
    }

    void add(Session s) { sessions.add(s); }

    // Safe to call from multiple threads while iterating
    void purgeExpired() {
        sessions.removeIf(Session::isExpired);
    }

    List<Session> active() {
        return sessions.stream().filter(s -> !s.isExpired()).toList();
    }
}

Verificação rápida

Qual exceção é lançada ao modificar um ArrayList durante a iteração?

Recapitulação: iteradores de falha rápida e de falha segura

Principais aprendizados:

  • Iteradores de falha rápida (ArrayList, HashMap) lançam ConcurrentModificationException quando ocorre uma modificação concorrente
  • Iteradores de falha segura (CopyOnWriteArrayList, ConcurrentHashMap) permitem modificações sem lançar exceções
  • Iterator.remove() é a única maneira segura de remover elementos durante uma iteração de falha rápida
  • removeIf() é a alternativa moderna e mais simples ao uso manual de Iterator.remove()
  • CopyOnWriteArrayList: seguro em cenários com muitas leituras e poucas gravações
  • ConcurrentHashMap: fracamente consistente — não lança CME e pode ou não observar modificações concorrentes

Perguntas Frequentes

A aula “Iterators Fail-Fast versus Fail-Safe” é grátis?

Sim — o texto completo de “Iterators Fail-Fast versus Fail-Safe” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Java Academy, atualize para CoddyKit PRO. O curso de Java Academy inclui 4 aulas no total.

O que vou aprender em “Iterators Fail-Fast versus Fail-Safe”?

Compreenda ConcurrentModificationException, o comportamento fail-fast e quando usar iterators baseados em cópias instantâneas. Você pratica Java Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Java Academy?

Nenhuma experiência prévia é necessária. Java Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Iterators Fail-Fast versus Fail-Safe”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Java Academy?

Sim. Cada aula de Java Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Contratos de Iterable e Iterator
  2. Implementação de um Iterator Personalizado
  3. ListIterator e Percurso Bidirecional
  4. Iterators Fail-Fast versus Fail-Safe
← Voltar para Java Academy