¿Por qué no obtengo una java.util.ConcurrentModificationException en este ejemplo?

Nota: Iterator#remove() método Iterator#remove() .

En el siguiente ejemplo de código, no entiendo por qué List.remove en el método main arroja ConcurrentModificationException , pero no en el método remove .

 public class RemoveListElementDemo { private static final List integerList; static { integerList = new ArrayList(); integerList.add(1); integerList.add(2); integerList.add(3); } public static void remove(Integer toRemove) { for(Integer integer : integerList) { if(integer.equals(toRemove)) { integerList.remove(integer); } } } public static void main(String... args) { remove(Integer.valueOf(2)); Integer toRemove = Integer.valueOf(3); for(Integer integer : integerList) { if(integer.equals(toRemove)) { integerList.remove(integer); } } } } 

He aquí por qué: Como está escrito en el Javadoc:

Los iteradores devueltos por los métodos iterator y listIterator de esta clase no son rápidos: si la lista se modifica estructuralmente en cualquier momento después de que se crea el iterador, de cualquier forma excepto a través de los propios métodos remove o add del iterador, el iterador generará una ConcurrentModificationException.

Esta comprobación se realiza en el método next() del iterador (como se puede ver en stacktrace). Pero alcanzaremos el método next() solo si hasNext() entregado verdadero, que es lo que llaman para cada uno para verificar si se cumple el límite. En su método remove, cuando hasNext() comprueba si necesita devolver otro elemento, verá que devolvió dos elementos, y ahora después de que se eliminó un elemento, la lista solo contiene dos elementos. Entonces todo es color de rosa y hemos terminado con la iteración. La verificación de modificaciones concurrentes no ocurre, ya que esto se hace en el método next() que nunca se llama.

Luego llegamos al segundo ciclo. Después de eliminar el segundo número, el método hasNext volverá a verificar si puede devolver más valores. Ya ha devuelto dos valores, pero la lista ahora solo contiene uno. Pero el código aquí es:

 public boolean hasNext() { return cursor != size(); } 

1! = 2, por lo que continuamos con el método next() , que ahora se da cuenta de que alguien ha estado jugando con la lista y dispara la excepción.

Espero que aclare tu pregunta.

Resumen

List.remove() no lanzará ConcurrentModificationException cuando elimine el segundo último elemento de la lista.

Una forma de solucionarlo es eliminar algo de una copia de una Collection (no de la Colección), si corresponde. Clone la colección original para hacer una copia a través de un Constructor .

Esta excepción puede ser lanzada por métodos que han detectado la modificación concurrente de un objeto cuando tal modificación no es permisible.

Para su caso específico, primero, no creo que el final sea ​​un camino a seguir teniendo en cuenta que tiene la intención de modificar la lista anterior a la statement

 private static final List integerList; 

También considere modificar una copia en lugar de la lista original.

 List copy = new ArrayList(integerList); for(Integer integer : integerList) { if(integer.equals(remove)) { copy.remove(integer); } } 

El método de avance / iterador no funciona cuando se eliminan elementos. Puede eliminar el elemento sin error, pero obtendrá un error de tiempo de ejecución cuando intente acceder a los elementos eliminados. No puede usar el iterador porque, como muestra agresivo, causará una ConcurrentModificationException, por lo tanto, utilice un ciclo for para regular, pero retroceda un paso a través de él.

 List integerList; integerList = new ArrayList(); integerList.add(1); integerList.add(2); integerList.add(3); int size= integerList.size(); //Item to remove Integer remove = Integer.valueOf(3); 

Una solución:

Recorre la matriz en orden inverso si vas a eliminar un elemento de la lista. Simplemente yendo hacia atrás por la lista, evita visitar un artículo que se ha eliminado, lo que elimina la excepción.

 //To remove items from the list, start from the end and go backwards through the arrayList //This way if we remove one from the beginning as we go through, then we will avoid getting a runtime error //for java.lang.IndexOutOfBoundsException or java.util.ConcurrentModificationException as when we used the iterator for (int i=size-1; i> -1; i--) { if (integerList.get(i).equals(remove) ) { integerList.remove(i); } } 

Este fragmento arrojará siempre una ConcurrentModificationException.

La regla es “No se puede modificar (agregar o eliminar elementos de la lista) mientras se itera sobre ella usando un Iterador (lo que sucede cuando se usa un bucle for-each)”.

JavaDocs:

Los iteradores devueltos por los métodos iterator y listIterator de esta clase no son rápidos: si la lista se modifica estructuralmente en cualquier momento después de que se crea el iterador, de cualquier forma excepto a través de los propios métodos remove o add del iterador, el iterador generará una ConcurrentModificationException.

Por lo tanto, si desea modificar la lista (o cualquier colección en general), utilice el iterador, porque entonces está al tanto de las modificaciones y, por lo tanto, se manejarán correctamente.

Espero que esto ayude.

Tenía el mismo problema, pero en caso de que estuviera agregando un elemento en la lista iterada. Lo hice de esta manera

 public static void remove(Integer remove) { for(int i=0; i 

Ahora todo va bien porque no crea ningún iterador sobre su lista, itera sobre él "manualmente". Y la condición i < integerList.size() nunca te engañará porque cuando eliminas / agregas algo en el tamaño de la lista de la lista decrece / incrementa ...

Espero que ayude, para mí esa fue la solución.

Esto funciona bien en Java 1.6

~% javac RemoveListElementDemo.java
~% java RemoveListElementDemo
~% cat RemoveListElementDemo.java

 import java.util.*; public class RemoveListElementDemo { private static final List integerList; static { integerList = new ArrayList(); integerList.add(1); integerList.add(2); integerList.add(3); } public static void remove(Integer remove) { for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } public static void main(String... args) { remove(Integer.valueOf(2)); Integer remove = Integer.valueOf(3); for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } } 

~%

Si usa colecciones de copiado sobre escritura, funcionará; sin embargo, cuando use list.iterator (), el iterador devuelto siempre hará referencia a la colección de elementos tal como estaba cuando (como a continuación) se llamó a list.iterator (), incluso si otro hilo modifica la colección. Cualquier método de mutación llamado en un iterador basado en escritura o en un ListIterator (como add, set o remove) arrojará una UnsupportedOperationException.

 import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class RemoveListElementDemo { private static final List integerList; static { integerList = new CopyOnWriteArrayList<>(); integerList.add(1); integerList.add(2); integerList.add(3); } public static void remove(Integer remove) { for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } public static void main(String... args) { remove(Integer.valueOf(2)); Integer remove = Integer.valueOf(3); for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } } 

Cambie el iterador for each for loop resuelva.

Y la razón es:

Los iteradores devueltos por los métodos iterator y listIterator de esta clase no son rápidos: si la lista se modifica estructuralmente en cualquier momento después de que se crea el iterador, de cualquier forma excepto a través de los propios métodos remove o add del iterador, el iterador generará una ConcurrentModificationException.

–Documentos de Java referidos.

En mi caso, lo hice así:

 int cursor = 0; do { if (integer.equals(remove)) integerList.remove(cursor); else cursor++; } while (cursor != integerList.size()); 

Verifica tu código hombre ….

En el método principal está tratando de eliminar el 4 ° elemento que no está allí y, por lo tanto, el error. En el método remove () está tratando de eliminar el tercer elemento que está allí y, por lo tanto, no hay ningún error.