Iterador a prueba de fallas vs iterador rápido a fallas

1. Introducción

En este artículo, presentaremos el concepto de iteradores Fail-Fast y Fail-Safe .

Los sistemas Fail-Fast abortan la operación lo más rápido posible, exponiendo las fallas inmediatamente y deteniendo toda la operación.

Mientras que los sistemas a prueba de fallas no abortan una operación en caso de falla. Estos sistemas tratan de evitar en la medida de lo posible la aparición de fallos.

2. Iteradores a prueba de fallos

Los iteradores a prueba de fallas en Java no funcionan cuando se modifica la colección subyacente.

Las colecciones mantienen un contador interno llamado modCount . Cada vez que se agrega o elimina un elemento de la colección , este contador se incrementa.

Al iterar, en cada llamada next () , el valor actual de modCount se compara con el valor inicial. Si hay una falta de coincidencia, lanza ConcurrentModificationException que aborta toda la operación.

Los iteradores predeterminados para las colecciones del paquete java.util como ArrayList , HashMap , etc. son Fail-Fast.

ArrayList numbers = // ... Iterator iterator = numbers.iterator(); while (iterator.hasNext()) { Integer number = iterator.next(); numbers.add(50); }

En el fragmento de código anterior, la ConcurrentModificationException se lanza al comienzo de un siguiente ciclo de iteración después de que se realizó la modificación.

No se garantiza que el comportamiento Fail-Fast ocurra en todos los escenarios, ya que es imposible predecir el comportamiento en caso de modificaciones simultáneas. Estos iteradores lanzan ConcurrentModificationException sobre la base del mejor esfuerzo .

Si durante la iteración a través de una colección , se elimina un elemento utilizando iterador 's remove () método, que es totalmente seguro y no una excepción .

Sin embargo, si la colección 's remove () se utiliza método para eliminar un elemento, se produce una excepción:

ArrayList numbers = // ... Iterator iterator = numbers.iterator(); while (iterator.hasNext()) { if (iterator.next() == 30) { iterator.remove(); // ok! } } iterator = numbers.iterator(); while (iterator.hasNext()) { if (iterator.next() == 40) { numbers.remove(2); // exception } }

3. Iteradores a prueba de fallos

Los iteradores a prueba de fallas favorecen la falta de fallas sobre el inconveniente del manejo de excepciones.

Esos iteradores crean un clon de la colección real y lo repiten. Si ocurre alguna modificación después de que se crea el iterador, la copia aún permanece intacta. Por lo tanto, estos iteradores continúan recorriendo la colección incluso si se modifica.

Sin embargo, es importante recordar que no existe un iterador verdaderamente a prueba de fallas. El término correcto es Débilmente consistente.

Eso significa que si una colección se modifica mientras se repite, lo que ve el iterador está débilmente garantizado . Este comportamiento puede ser diferente para diferentes Colecciones y está documentado en Javadocs de cada una de esas Colecciones .

Sin embargo, los iteradores a prueba de fallas tienen algunas desventajas. Una desventaja es que no se garantiza que el iterador devuelva datos actualizados de la colección , ya que está trabajando en el clon en lugar de en la colección real .

Otra desventaja es la sobrecarga de crear una copia de la Colección , tanto en tiempo como en memoria.

Los iteradores de colecciones del paquete java.util.concurrent , como ConcurrentHashMap , CopyOnWriteArrayList , etc., son de naturaleza a prueba de fallas .

ConcurrentHashMap map = new ConcurrentHashMap(); map.put("First", 10); map.put("Second", 20); map.put("Third", 30); map.put("Fourth", 40); Iterator iterator = map.keySet().iterator(); while (iterator.hasNext()) { String key = iterator.next(); map.put("Fifth", 50); }

En el fragmento de código anterior, usamos un iterador a prueba de fallas . Por lo tanto, aunque se agrega un nuevo elemento a la Colección durante la iteración, no genera una excepción.

El iterador predeterminadopara el ConcurrentHashMap es débilmente consistente. Esto significa que este iterador puede tolerar modificaciones concurrentes, atraviesa elementos tal como existían cuando se construyó el iterador y puede (pero no se garantiza que lo haga) reflejar modificaciones a la colección después de la construcción del iterador .

Por lo tanto, en el fragmento de código anterior, la iteración se repite cinco veces, lo que significa que detecta el elemento recién agregado a la colección .

4. Conclusión

En este tutorial, hemos visto qué significan los iteradores Fail-Safe y Fail-Fast y cómo se implementan en Java.

El código completo presentado en este artículo está disponible en GitHub.