jdk descargar java

descargar - java offline



¿Por qué este código no arroja una ConcurrentModificationException? (3)

Esta pregunta ya tiene una respuesta aquí:

¿Por qué este código no arroja una ConcurrentModificationException ? Modifica una Collection mientras itera a través de ella, sin usar el método Iterator.remove() , que es la única forma segura de eliminar .

List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C")); for (String string : strings) if ("B".equals(string)) strings.remove("B"); System.out.println(strings);

Obtengo el mismo resultado si reemplazo el ArrayList con un LinkedList . Sin embargo, si cambio la lista a ("A", "B", "C", "D) o simplemente ("A", "B") obtengo la excepción como se esperaba. ¿Qué está pasando? Estoy usando jdk1.8.0_25 si eso es relevante.

EDITAR

He encontrado el siguiente enlace

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4902078

La sección relevante es

La solución ingenua es agregar comprobaciones de comodificación a hasNext en AbstractList, pero esto duplica el costo de la comprobación de comodificación. Resulta que es suficiente hacer la prueba solo en la última iteración, lo que no agrega prácticamente nada al costo. En otras palabras, la implementación actual de hasNext:

public boolean hasNext() { return nextIndex() < size; }

Se reemplaza por esta implementación:

public boolean hasNext() { if (cursor != size()) return true; checkForComodification(); return false; }

Este cambio no se realizará porque un organismo regulador interno de Sun lo rechazó. El fallo formal indicó que el cambio "ha demostrado el potencial de tener un impacto de compatibilidad significativo sobre el código existente". (El "impacto de compatibilidad" es que la solución tiene el potencial de reemplazar el mal comportamiento silencioso con una ConcurrentModificationException).


@Tavian Barnes tiene toda la razón. No se puede garantizar que se produzca esta excepción si la modificación concurrente en cuestión no está sincronizada. Citando de la especificación java.util.ConcurrentModification :

Tenga en cuenta que el comportamiento a prueba de fallas no puede garantizarse ya que, en términos generales, es imposible hacer garantías duras en presencia de modificaciones concurrentes no sincronizadas. Las operaciones a prueba de fallas lanzan ConcurrentModificationException con el mejor esfuerzo. Por lo tanto, sería un error escribir un programa que dependiera de esta excepción para su corrección: ConcurrentModificationException debería usarse solo para detectar errores.

Enlace a JavaDoc para ConcurrentModificationException


Como regla general, las ConcurrentModificationException se lanzan cuando se detecta la modificación, no se causa . Si nunca accede al iterador después de la modificación, no arrojará una excepción. Este detalle minucioso hace que ConcurrentModificationException sea ​​bastante poco confiable para detectar el mal uso de las estructuras de datos, desafortunadamente, ya que solo se lanzan después de que se ha hecho el daño.

Este escenario no arroja una ConcurrentModificationException porque next() no recibe una llamada en el iterador creado después de la modificación.

Los bucles For-each son realmente iteradores, por lo que su código realmente se ve así:

List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C")); Iterator<String> iter = strings.iterator(); while(iter.hasNext()){ String string = iter.next(); if ("B".equals(string)) strings.remove("B"); } System.out.println(strings);

Considere que su código se ejecuta en la lista que proporcionó. Las iteraciones se ven así:

  1. hasNext() devuelve true, enter loop, -> iter se mueve al índice 0, string = "A", no eliminado
  2. hasNext() devuelve verdadero, continuar bucle -> iter se mueve al índice 1, cadena = "B", eliminado. strings ahora tiene longitud 2.
  3. hasNext() devuelve falso (iter está actualmente en el último índice, no quedan más índices), salir del bucle.

Por lo tanto, a medida que se lanzan ConcurrentModificationException s cuando una llamada a next() detecta que se ha realizado una modificación, este escenario evita por poco esa excepción.

Para sus otros dos resultados, tenemos excepciones. Para "A", "B", "C", "D" , después de eliminar "B" todavía estamos en el bucle, y next() detecta la ConcurrentModificationException modificación ConcurrentModificationException , mientras que para "A", "B" me imagino es una especie de ArrayIndexOutOfBounds que se captura y se vuelve a lanzar como una ConcurrentModificationException


hasNext en el iterador de ArrayList es solo

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

Después de la llamada de remove , el iterador está en el índice 2 y el tamaño de la lista es 2, por lo que informa que la iteración se ha completado. Sin verificación de modificación concurrente. Con ("A", "B", "C", "D) o (" A "," B "), el iterador no está en el nuevo final de la lista, por lo que se llama al next , y eso arroja la excepción .

ConcurrentModificationException s son solo una ayuda de depuración. No puedes confiar en ellos.