2012-06-04 16 views
11

Enumeration no tira ConcurrentModificationException, ¿por qué?Enumeración Java vs iterador

Vea el siguiente código.

public static void main(String[] args) { 

    Vector<String> v=new Vector<String>(); 
    v.add("Amit"); 
    v.add("Raj"); 
    v.add("Pathak"); 
    v.add("Sumit"); 
    v.add("Aron"); 
    v.add("Trek"); 

    Enumeration<String> en=v.elements(); 

    while(en.hasMoreElements()) 
    { 
     String value=(String) en.nextElement(); 
     System.out.println(value); 
     v.remove(value); 

    } 

} 

sólo se imprime:

 
Amit 
Pathak 
Aron 

¿Por qué es este tipo de comportamiento. ¿Podemos decir que Enumerator es seguro para subprocesos?

Edit: Cuando se trabaja con Iterator arroja ConcurrentModificationException en la aplicación de un solo hilo.

public static void main(String[] args) { 

    Vector<String> v=new Vector<String>(); 
    v.add("Amit"); 
    v.add("Raj"); 
    v.add("Pathak"); 
    v.add("Sumit"); 
    v.add("Aron"); 
    v.add("Trek"); 

    Iterator<String> it=v.iterator(); 
    while(it.hasNext()) 
    { 
     String value=(String) it.next(); 
     System.out.println(value); 
     v.remove(value); 
    } 
} 

Compruebe.

+1

Lea http://stackoverflow.com/questions/948194/different-using-java-util-enumeration-and-iterator – Garbage

+0

Cuál es el trasfondo de esta pregunta? ¿Está considerando usar enumeraciones en lugar de iteradores en un entorno de múltiples subprocesos? –

+0

¿Hay algún otro código que debamos conocer? No hay multiprocesamiento en el código de ejemplo anterior, así que a menos que tenga una razón específica para culpar a la seguridad del hilo, creo que está un poco fuera de rumbo aquí – posdef

Respuesta

4

Enumeración no lanza ConcurrentModificationException, ¿por qué?

Porque no hay ninguna ruta en el código invocado que arroja esta excepción. Edit: Me refiero a la implementación proporcionada por la clase Vector, no sobre la interfaz Enumeration en general.

¿Por qué es este comportamiento? ¿Podemos decir que Enumerator es seguro para subprocesos?

Es seguro para subprocesos en un sentido que el código que se ejecuta es sincronizado correctamente. Sin embargo, no creo que el resultado que rinda tu bucle sea lo que podrías esperar.

El motivo de su salida es que el objeto Enumeration mantiene un contador, que se incrementa después de cada invocación de nextElement(). Este contador no tiene conocimiento de su invocación de remove().

+0

Según tengo entendido, la enumeración tiene referencia a la colección como Iterator tiene. Tan pronto como eliminemos el elemento del vector, lo notaremos en Enumeración. Entonces, ¿por qué está imprimiendo 3? No todos 5 si estamos imprimiendo primero y luego eliminando. – amicngh

+0

Ese es el punto. La enumeración no detecta su invocación de eliminar (lo que haría necesario disminuir su índice de variable). – MartinK

-1

retire la v.remove(value) y todo funcionará como se espera

Editar: lo siento, leído mal la pregunta no

Esto no tiene nada que ver con threadsafety sin embargo. Ni siquiera está realizando subprocesos múltiples, por lo que no hay ninguna razón por la cual Java arroje una excepción para esto.

Si desea excepciones cuando se cambia el vector que sea inmodificable

+4

Esto no parece ser una respuesta a la pregunta –

+0

Votos negativos eliminados después de la edición .. –

10

Tenga en cuenta que ConcurrentModificationException no tiene nada que ver con la concurrencia en el sentido de multihilo o seguridad de subprocesos. Algunas colecciones permiten modificaciones concurrentes, otras no. Normalmente puede encontrar la respuesta en los documentos. Pero concurrente no significa concurrentemente por diferentes hilos. Significa que puedes modificar la colección mientras iteras.

ConcurrentHashMap es un caso especial, ya que está explícitamente definido como seguro para subprocesos Y editable mientras se lo itera (lo que creo que es cierto para todas las colecciones seguras para subprocesos).

De todos modos, siempre que utilice un solo hilo para iterar y modificar la colección, ConcurrentHashMap es la solución incorrecta para su problema. Estás utilizando la API incorrectamente. Debe usar Iterator.remove() para eliminar elementos. Alternativamente, puede hacer una copia de la colección antes de iterar y modificar el original.

EDIT:

No sé de cualquier enumeración que lanza una ConcurrentModificationException. Sin embargo, el comportamiento en el caso de una modificación simultánea puede no ser lo que espera que sea. Como ve en su ejemplo, la enumeración omite cada segundo elemento en la lista. Esto se debe a que su índice interno se incrementa independientemente de las eliminaciones. Así que esto es lo que sucede:

  • en.nextElement() - devuelve primer elemento de Vector, incrementa índice a 1
  • v.remove (valor) - elimina primer elemento de Vector, desplaza todos los elementos dejaron
  • en.nextElement() - devuelve el segundo elemento del vector, que ahora es "Pathak"

El comportamiento a prueba de ayuno de iterador le protege de este tipo de cosas, por lo que en general es preferible a Enumberation. En su lugar, usted debe hacer lo siguiente:

Iterator<String> it=v.iterator(); 
while(it.hasNext()) 
{ 
    String value=(String) it.next(); 
    System.out.println(value); 
    it.remove(); // not v.remove(value); !! 
} 

alternativa:

for(String value : new Vector<String>(v)) // make a copy 
{ 
    String value=(String) it.next(); 
    System.out.println(value); 
    v.remove(value); 
} 

La primera de ellas es ciertamente preferible, ya que realmente no necesita la copia, siempre y cuando se utiliza la API, ya que es destinado a.

+0

Finalmente, entiendo que Enumeration nunca lanza ConcurrentModificationException? – amicngh

+1

+1 para "ConcurrentModificationException no tiene nada que ver con concurrencia en el sentido de multihilo o seguridad de subprocesos" –

3

La modificación concurrente aquí no tiene nada que ver con los hilos.

Concurrencia aquí simplemente significa que está modificando la colección mientras que está iterando sobre ella. (En el ejemplo, esto ocurre en el mismo hilo.)

iteradores y enumeraciones de colecciones puede lanzar ConcurrentModificationException en este caso, pero no tiene que hacerlo. Los que muestran fallan comportamiento. Aparentemente, la enumeración de Vector no es rápida.

Thread-safety obviamente implica varios hilos de alguna manera. Vector es seguro para subprocesos solo en el sentido de que sus operaciones (como agregar, obtener, etc.) están sincronizadas. Esto es para evitar el comportamiento no determinista cuando un hilo está agregando un elemento mientras que al mismo tiempo otro hilo intenta eliminar uno, por ejemplo.

Ahora, cuando un subproceso modifica estructuralmente su colección mientras que otro subproceso se itera sobre él, debe ocuparse tanto de la seguridad del subproceso como de los problemas de modificación concurrente. En este caso, y probablemente en general, es más seguro no confiar en que ConcurrentModificationException haya sido arrojado. Lo mejor es elegir la implementación de recopilación adecuada (por ejemplo, una ejecución segura de subprocesos) y evitar/deshabilitar la modificación simultánea.

Algunos iteradores permiten agregar/configurar/eliminar elementos a través del propio iterador. Esta puede ser una buena alternativa si realmente necesita una modificación simultánea.

1

Respuesta corta: Es un bonus feature que se inventó después de que la enumeración ya estaba allí, por lo que el hecho de que el enumerador no lo arroje no sugiere nada en particular.

Respuesta larga:
de Wikipedia: implementaciones

Collection en pre-JDK 1.2 [...] no contenía un marco colecciones. Los métodos estándar para agrupar objetos Java se realizaron a través de la matriz, el Vector y las clases Hashtable, , que desafortunadamente no fueron fáciles de extender, y no implementaron una interfaz de miembro estándar . Para abordar la necesidad de estructuras de datos de recopilación reutilizables [...] El marco de colecciones fue diseñado y desarrollado principalmente por Joshua Bloch, y se introdujo en JDK 1.2.

Cuando el equipo Bloch hizo eso, pensaron que era una buena idea poner en un nuevo mecanismo que envía una alarma (ConcurrentModificationException) cuando sus colecciones no se sincronizaron correctamente en un programa multi-hilo. Hay dos cosas importantes que se deben tener en cuenta sobre este mecanismo: 1) no se garantiza la captura de errores de concurrencia; la excepción solo se lanza si tiene suerte. 2) La excepción también se arroja si usa mal la colección con un solo hilo (como en su ejemplo).

Por lo tanto, una colección que no arroje un ConcurrentModificationException cuando se accede por varios subprocesos tampoco implica que sea segura para subprocesos.

0

Depende de cómo obtiene la enumeración. Véase el siguiente ejemplo, no tirar ConcurrentModificationException:

import java.util.*; 

public class ConcurrencyTest { 
    public static void main(String[] args) { 

     Vector<String> v=new Vector<String>(); 
     v.add("Amit"); 
     v.add("Raj"); 
     v.add("Pathak"); 
     v.add("Sumit"); 
     v.add("Aron"); 
     v.add("Trek"); 

     Enumeration<String> en=Collections.enumeration(v);//v.elements(); 

     while(en.hasMoreElements()) 
     { 
      String value=(String) en.nextElement(); 
      System.out.println(value); 
      v.remove(value); 
     }    

     System.out.println("************************************"); 

     Iterator<String> iter = v.iterator(); 
      while(iter.hasNext()){ 
       System.out.println(iter.next()); 
       iter.remove(); 
       System.out.println(v.size()); 
     } 
    } 
} 

enumeración es sólo una interfaz, que es el comportamiento real depende de la implementación. La enumeración de la llamada Collections.enumeration() envuelve el iterador de alguna manera, por lo que no es rápido, pero la enumeración obtenida al llamar a Vector.elements() no lo es.

La enumeración non-fail-fast podría introducir comportamiento arbitrario, no determinista en un momento indeterminado en el futuro. Por ejemplo: si escribe el método principal como este, lanzará java.util.NoSuchElementException después de la primera iteración.

public static void main(String[] args) { 

     Vector<String> v=new Vector<String>(); 
     v.add("Amit"); 
     v.add("Raj"); 
     v.add("Pathak"); 

     Enumeration<String> en = v.elements(); //Collections.enumeration(v); 

     while(en.hasMoreElements()) 
     { 
      v.remove(0); 
      String value=(String) en.nextElement(); 
      System.out.println(value); 
     }  
    }