¿Cómo evitar "ConcurrentModificationException"mientras se eliminan elementos de 'ArrayList' mientras se itera? [duplicar]


Esta pregunta ya tiene una respuesta aquí:

Estoy tratando de eliminar algunos elementos de un ArrayList mientras lo itera de esta manera:

for (String str : myArrayList) {
    if (someCondition) {
        myArrayList.remove(str);
    }
}

Por supuesto, obtengo un ConcurrentModificationException cuando intento eliminar elementos de la lista al mismo tiempo que iterando myArrayList. ¿Hay alguna solución simple para resolver este problema?

Author: ʇolɐǝz ǝɥʇ qoq, 2013-08-26

10 answers

Utilice un Iterator y llamar remove():

Iterator<String> iter = myArrayList.iterator();

while (iter.hasNext()) {
    String str = iter.next();

    if (someCondition)
        iter.remove();
}
 466
Author: arshajii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-26 16:29:00

Como alternativa a las respuestas de todos los demás, siempre he hecho algo como esto:

List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
    if (someCondition) {
        toRemove.add(str);
    }
}
myArrayList.removeAll(toRemove);

Esto evitará que tenga que lidiar con el iterador directamente, pero requiere otra lista. Siempre he preferido esta ruta por cualquier razón.

 146
Author: Kevin DiTraglia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-09 04:19:12

El usuario de Java 8 puede hacer eso: list.removeIf(...)

    List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
    list.removeIf(e -> (someCondition));

Eliminará elementos de la lista, para los cuales se cumple alguna condición

 65
Author: Mikhail Boyarsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-26 12:11:58

Tienes que usar el método remove () del iterador, lo que significa que no hay bucle for mejorado:

for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
    iterator.next();
    if (someCondition) {
        iterator.remove();
    }
}
 55
Author: Eric Stein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-16 20:54:48

No, no, NO!

En tareas individuales no necesita usar Iterator, además, CopyOnWriteArrayList (debido a un golpe de rendimiento).

La solución es mucho más simple: intente usar el bucle canónico for en lugar de for-each loop.

De acuerdo con los propietarios de derechos de autor de Java (hace algunos años Sun, ahora Oracle) para-cada guía de bucle, utiliza iterador para recorrer la colección y solo lo oculta para hacer que el código se vea mejor. Pero, por desgracia, como podemos ver, produjo más problemas que beneficios, de lo contrario este tema no se plantearía.

Por ejemplo, este código llevará a java.útil.ConcurrentModificationException al ingresar la siguiente iteración en ArrayList modificada:

        // process collection
        for (SomeClass currElement: testList) {

            SomeClass founDuplicate = findDuplicates(currElement);
            if (founDuplicate != null) {
                uniqueTestList.add(founDuplicate);
                testList.remove(testList.indexOf(currElement));
            }
        }

Pero el siguiente código funciona bien:

    // process collection
    for (int i = 0; i < testList.size(); i++) {
        SomeClass currElement = testList.get(i);

        SomeClass founDuplicate = findDuplicates(currElement);
        if (founDuplicate != null) {
            uniqueTestList.add(founDuplicate);
            testList.remove(testList.indexOf(currElement));
            i--; //to avoid skipping of shifted element
        }
    }

Por lo tanto, trate de utilizar el enfoque de indexación para iterar sobre las colecciones y evitar para-cada bucle, ya que no son equivalentes! For-each loop utiliza algunos iteradores internos, que comprueban la modificación de la colección y el tiro Excepción ConcurrentModificationException. Para confirmar esto, eche un vistazo más de cerca a la traza de pila impresa cuando use el primer ejemplo que he publicado:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
    at java.util.AbstractList$Itr.next(AbstractList.java:343)
    at TestFail.main(TestFail.java:43)

Para multihilo utilice los enfoques multitarea correspondientes (como palabra clave sincronizada).

 26
Author: Dima Naychuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-24 01:05:18

Mientras que otras soluciones sugeridas funcionan, si realmente desea que la solución sea segura para el hilo, debe reemplazar ArrayList con CopyOnWriteArrayList

    //List<String> s = new ArrayList<>(); //Will throw exception
    List<String> s = new CopyOnWriteArrayList<>();
    s.add("B");
    Iterator<String> it = s.iterator();
    s.add("A");

    //Below removes only "B" from List
    while (it.hasNext()) {
        s.remove(it.next());
    }
    System.out.println(s);
 8
Author: Prashant Bhate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-26 16:55:51

Un método alternativo es convertir su List a array, iterarlos y eliminarlos directamente de la List basado en su lógica.

List<String> myList = new ArrayList<String>(); // You can use either list or set

myList.add("abc");
myList.add("abcd");
myList.add("abcde");
myList.add("abcdef");
myList.add("abcdefg");

Object[] obj = myList.toArray();

for(Object o:obj)  {
    if(condition)
        myList.remove(o.toString());
}
 7
Author: CarlJohn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-21 12:50:20

Si desea modificar su Lista durante el recorrido, debe usar Iterator. Y luego puede usar iterator.remove() para eliminar los elementos durante el recorrido.

 6
Author: Juned Ahsan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-26 16:28:25
List myArrayList  = Collections.synchronizedList(new ArrayList());

//add your elements  
 myArrayList.add();
 myArrayList.add();
 myArrayList.add();

synchronized(myArrayList) {
    Iterator i = myArrayList.iterator(); 
     while (i.hasNext()){
         Object  object = i.next();
     }
 }
 6
Author: Prabhakaran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-26 16:48:55

Puede usar la función remove() del iterador para eliminar el objeto del objeto de colección subyacente. Pero en este caso puede eliminar el mismo objeto y no cualquier otro objeto de la lista.

Desde aquí

 1
Author: gilo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-22 16:30:43