remove modification from concurrentmodificationexception concurrent avoid java iterator removechild concurrentmodification

modification - iterator remove java



java.util.ConcurrentModificationException con iterador (4)

El iterador verifica la propiedad a prueba de fallas para cualquier modificación en la estructura de la colección subyacente cada vez que intentamos obtener el siguiente elemento. Si se encuentran modificaciones, arroja ConcurrentModificationException. Todas las implementaciones de Iterator en las clases de Colección son rápidas de fallas por diseño, excepto las clases de colección concurrentes como ConcurrentHashMap y CopyOnWriteArrayList.

Fuente: Google

Lo entenderás mejor con un ejemplo escrito a continuación:

import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; public class IteratorExp { public static void main(String... q) { //CASE - ONE List<String> strList = new ArrayList<>(Arrays.asList("a", "b", "c")); Iterator<String> itr = strList.iterator(); /* * strList.add("e"); strList.add("f"); strList.add("g"); */ while (itr.hasNext()) { System.out.println(itr.next()); } /* * Exception in thread "main" java.util.ConcurrentModificationException * at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at * java.util.ArrayList$Itr.next(Unknown Source) at * IteratorExp.main(IteratorExp.java:14) */ //CASE - TWO List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)); Iterator<Integer> itrOne = intList.iterator(); Iterator<Integer> itrTwo = intList.iterator(); for (; itrOne.hasNext();) { if (itrOne.next().equals(5)) { itrOne.remove(); // #1 //intList.remove(itrOne.next()); // #2 } } for (; itrTwo.hasNext();) { if (itrTwo.next().equals(5)) { itrTwo.remove(); // #1 //intList.remove(itrTwo.next()); // #2 } } /* * Exception in thread "main" java.util.ConcurrentModificationException * at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at * java.util.ArrayList$Itr.next(Unknown Source) at * IteratorExp.main(IteratorExp.java:35) */ } }

Sé que si intentaría eliminar de la recopilación en bucle a través de él con el bucle simple obtendré esta excepción: java.util.ConcurrentModificationException . Pero estoy usando Iterator y todavía me genera esta excepción. ¿Alguna idea de por qué y cómo resolverlo?

HashSet<TableRecord> tableRecords = new HashSet<>(); ... for (Iterator<TableRecord> iterator = tableRecords.iterator(); iterator.hasNext(); ) { TableRecord record = iterator.next(); if (record.getDependency() == null) { for (Iterator<TableRecord> dependencyIt = tableRecords.iterator(); dependencyIt.hasNext(); ) { TableRecord dependency = dependencyIt.next(); //Here is the line which throws this exception if (dependency.getDependency() != null && dependency.getDependency().getId().equals(record.getId())) { tableRecords.remove(record); } } } }


Debe usar iterator.remove() lugar de tableRecords.remove()

Puede eliminar elementos de una lista en la que iterar solo si utiliza el método de eliminación del iterador.

EDITAR:

Cuando crea un iterador, comienza a contar las modificaciones que se aplicaron en la colección. Si el iterador detecta que se hicieron algunas modificaciones sin usar su método (o usar otro iterador en la misma colección), ya no puede garantizar que no pasará dos veces en el mismo elemento o se salteará uno, por lo que lanza esta excepción.

Significa que necesita cambiar su código para que solo elimine elementos a través de iterator.remove (y con un solo iterador)

O

haga una lista de elementos para eliminar y luego elimínelos después de que haya terminado de iterar.


El contrato para el iterador de HashSet es que no se puede eliminar del hashset más que a través del método de eliminación de ese iterador específico. Desde la perspectiva de dependencyIt , ha eliminado un elemento que no sea llamando a su método de remove para que emita una ConcurrentModificationException .

Parece que desea eliminar los registros de su hashset cuando tienen el mismo ID de registro. ¿No sería más fácil anular los métodos equals y hashcode tus registros para asegurarte de que los registros con la misma ID igualen y tengan el mismo hashcode? (si eso tiene sentido por supuesto)


El problema es que tienes dos iteradores en el alcance al mismo tiempo y están "luchando" entre sí. La forma más sencilla de solucionar el problema es simplemente salir del bucle interno si encuentra una coincidencia:

for (Iterator<TableRecord> iterator = tableRecords.iterator(); iterator.hasNext(); ) { TableRecord record = iterator.next(); if (record.getDependency() == null) { for (Iterator<TableRecord> dependencyIt = tableRecords.iterator(); dependencyIt.hasNext(); ) { TableRecord dependency = dependencyIt.next(); //Here is the line which throws this exception if (dependency.getDependency() != null && dependency.getDependency().getId().equals(record.getId())) { iterator.remove(); break; // ADD THIS LINE } } } }

La intención de los Iterator Java es "fallar rápido" siempre que se cambie su contenedor subyacente sin haber sido modificado utilizando el Iterator . Está utilizando iteradores anidados, por lo que cualquier operación remove() emitida a uno hará que el otro lance una Exception si continúa usándose. Por esta razón, si necesita emitir un remove() , entonces deberá hacerlo en el iterador "externo" (lo que está haciendo) e interrumpir el uso del segundo iterador después (lo que hace la declaración de break agregada). ).