Me encontré con este pedazo de un código, que elimina el elemento de la lista, mientras que la iteración, y sin embargo, no lanza una excepción concurrente y hojas de algunos elementos de la lista.
¿Porqué es eso?
Ejemplo:.
public class Application {
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
for (Integer integer : integerList) {
removeInteger(integerList, integer);
}
System.out.println(integerList.toString()); // prints [2] as it was not removed (while it should be)
}
private static void removeInteger(List<Integer> integerList, Integer integer) {
for (Integer integer1 : integerList) {
if (integer.equals(integer1)) {
int index = integerList.indexOf(integer);
if (index >= 0) {
integerList.remove(index);
}
}
}
}
}
Si el cambio que removeInteger
el método a utilizar Iterator
en su lugar y en el main
paso por una copia de integerList
todo funciona como se esperaba.
En primer lugar si se agrega más elementos a la lista todavía fallará con una ConcurrentModificationException.
Por la razón que no falla con el ejemplo publicado, tenemos que echar un vistazo a lo que sucede internamente.
Si se depura se ve que en el ejemplo publicado, ambos bucles sólo son iterados vez. Extracción del elemento en el bucle interior ( removeInteger
método) agota ambos iteradores. Como no hay ninguna interacción adicional, es causado ningún error. 'Sólo' una llamada de Iterator#next
después de una modificación podría causar una ConcurrentModificationException.
Sí, pero ¿por qué se agota ellos?
Comprobación del código fuente de la ArrayList#Itr
que podemos ver que
public boolean hasNext() {
return cursor != size;
}
sólo comprueba una variable llamada size
. Esta variable size
es una propiedad de la ArrayList
. Cuando remove
se llama disminuye esta variable. Por lo tanto, la próxima vez que hasNext
se llama, el iterador asume que no hay más elementos nuevos.
Nota al margen: cursor
puntos al siguiente elemento de la lista.