Java 46: Iterators, Iterable & Fail-Fast vs Fail-Safe
easy⏱ 5 mincoursejava
Iterable e Iterator
Iterable<T> declara un método, Iterator<T> iterator(). Iterator<T> tiene hasNext(), next() y un remove() por defecto. El compilador convierte for (T x : c) en un while (it.hasNext()) { T x = it.next(); ... }. Implementa Iterable y tu tipo funciona en for-each, streams (StreamSupport) y spreads de varargs gratis.
for (String s : list) { ... }
// compiles to:
Iterator<String> it = list.iterator();
while (it.hasNext()) { String s = it.next(); ... }
Fail-fast y ConcurrentModificationException
La mayoría de colecciones de java.util (ArrayList, HashMap) son fail-fast: mantienen un modCount; el iterador lo fotografía y lanza ConcurrentModificationException en next() si la colección fue modificada estructuralmente durante la iteración (¡incluso desde el mismo hilo!). Es un detector de bugs best-effort, no una garantía de thread-safety. La solución: remover vía iterator.remove(), o usar removeIf.
for (String s : list) {
if (s.isBlank()) list.remove(s); // throws CME!
}
list.removeIf(String::isBlank); // correct
// or: Iterator it = list.iterator(); while(...) { it.remove(); }
Iteradores fail-safe
Las colecciones concurrentes son fail-safe: CopyOnWriteArrayList itera sobre un snapshot tomado al crear el iterador, así que las escrituras nunca lanzan — pero el iterador no verá escrituras posteriores. El iterador de ConcurrentHashMap es débilmente consistente: tolera escrituras concurrentes y puede o no reflejarlas. Fail-safe significa 'nunca lanza CME', no 've una vista perfectamente actual'.
Construye iteradores fail-fast vs fail-safe
Crea una FastList que rastree modCount; su iterador captura expectedModCount y lanza 'ConcurrentModificationException' en next() si divergen. Crea una SafeList cuyo iterador copie el arreglo de respaldo por adelantado. Itera ambas añadiendo un elemento a mitad de bucle: la fast lanza, la safe termina sobre el snapshot original. Implementa también un Range Iterable y súmalo con for-each.
removeIf sobre iteración manual
collection.removeIf(predicate) es la forma más limpia y rápida de borrar elementos coincidentes — usa el iterador internamente y evita la CME. Úsalo antes de escribir a mano un bucle Iterator.remove(); además las colecciones lo sobreescriben (ArrayList hace una sola pasada compactante).
Quiz: ¿CME es thread-safety?
¿La iteración fail-fast hace thread-safe a una colección? No. Los chequeos de modCount son best-effort y racy — la modificación concurrente puede perderse o lanzar de forma no determinista. La CME es una ayuda de depuración para mal uso en un solo hilo, no un control de concurrencia. Para concurrencia real usa ConcurrentHashMap/CopyOnWriteArrayList o sincronización externa.