2012-06-04 16 views
11

Enumeration non getta ConcurrentModificationException, perché?Enumerazione Java vs Iterator

Vedere sotto il codice.

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); 

    } 

} 

E 'solo stampe:

 
Amit 
Pathak 
Aron 

Perché questo è un comportamento del genere. Possiamo dire che Enumerator è thread-safe.

Modifica: Quando si lavora con Iterator, esso genera ConcurrentModificationException nell'applicazione a thread singolo.

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); 
    } 
} 

Si prega di controllare.

+1

Si prega di leggere http://stackoverflow.com/questions/948194/different-using-java-util-enumeration-and-iterator – Garbage

+0

Qual è lo sfondo di questa domanda? Stai pensando di utilizzare le enumerazioni anziché gli iteratori in un ambiente a più thread? –

+0

C'è altro codice da sapere? Non c'è il multithreading nel codice di esempio qui sopra, quindi a meno che tu non abbia una ragione specifica per incolpare la sicurezza dei thread penso che tu sia un po 'fuori pista qui – posdef

risposta

4

Enumeration perché non lancia ConcurrentModificationException, perché?

Perché non vi è alcun percorso nel codice richiamato che genera questa eccezione. Modifica: Mi riferisco all'implementazione fornita dalla classe Vector, non all'interfaccia Enumeration in generale.

Perché è questo un comportamento del genere. Possiamo dire che Enumerator è thread-safe.

è thread-safe in un senso che il codice eseguito è correttamente sincronizzati. Tuttavia, non penso che il risultato del tuo ciclo sia quello che vorresti, tranne.

Il motivo dell'output è che l'oggetto Enumeration gestisce un contatore, che viene incrementato dopo ogni richiamo di nextElement(). Questo contatore non è a conoscenza della tua chiamata di remove().

+0

La mia comprensione è Enumerazione ha riferimento alla collezione come ha Iterator. Appena togliamo l'elemento dal vettore, questo viene notato in Enumerazione. Quindi perché stampa 3 non tutti 5 se stampiamo prima e poi rimuoviamo. – amicngh

+0

Questo è il punto. L'Enumerazione non nota la tua invocazione di remove (che renderebbe necessario ridurre la sua variabile index). – MartinK

-1

rimuovere il v.remove(value) e tutto funzionerà come previsto

Edit: dispiace, frainteso la questione ci

Questo non ha nulla a che fare con threadsafety però. Non sei nemmeno multithreading quindi non c'è ragione per cui Java potrebbe fare un'eccezione per questo.

Se volete eccezioni quando si modifica il vettore renderlo immodificabile

+4

Questa non sembra essere una risposta alla domanda posta –

+0

Downvote rimosso dopo la modifica .. –

10

Si noti che ConcurrentModificationException non ha nulla a che fare con la concorrenza nel senso del multithreading o della sicurezza del thread. Alcune raccolte consentono modifiche simultanee, altre no. Normalmente puoi trovare la risposta nei documenti. Ma concurrent non significa simultaneamente da diversi thread. Significa che è possibile modificare la raccolta mentre si itera.

ConcurrentHashMap è un caso speciale, poiché è esplicitamente definito come thread-safe E modificabile mentre è iterato (che penso sia vero per tutte le raccolte thread-safe).

In ogni caso, fino a quando si sta utilizzando un singolo thread per scorrere e modificare la raccolta, ConcurrentHashMap è la soluzione sbagliata al vostro problema. Stai utilizzando l'API in modo errato. Dovresti usare Iterator.remove() per rimuovere gli oggetti. In alternativa, puoi creare una copia della raccolta prima di iterare e modificare l'originale.

EDIT:

Non so di alcun Enumeration che getta una ConcurrentModificationException. Tuttavia, il comportamento nel caso di una modifica simultanea potrebbe non essere quello che ci si aspetta che sia. Come vedi nell'esempio, l'enumerazione salta ogni secondo elemento nell'elenco. Ciò è dovuto al fatto che l'indice interno viene incrementato indipendentemente dalle rimuove. Quindi questo è ciò che accade:

  • en.nextElement() - restituisce primo elemento da Vector, incrementa indice per 1
  • v.remove (value) - rimuove primo elemento da Vector, sposta tutti gli elementi lasciati
  • en.nextElement() - restituisce secondo elemento dal vettore, che ora è "Pathak"

Il comportamento fail-fast di Iterator vi protegge da questo tipo di cose, è per questo che è generalmente preferibile Enumberation. Invece, si dovrebbe effettuare le seguenti operazioni:

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); 
} 

Il primo è certamente preferibile, in quanto non si ha realmente bisogno la copia fino a quando si utilizza l'API così com'è previsto.

+0

Infine capisco che Enumeration non lancia mai ConcurrentModificationException? – amicngh

+1

+1 per "ConcurrentModificationException non ha nulla a che fare con la concorrenza nel senso del multithreading o della sicurezza del thread" –

3

La modifica concomitante qui non ha nulla a che fare con i thread.

La concorrenza qui significa semplicemente che si sta modificando la raccolta mentre si sta iterando su di esso. (Nel tuo esempio questo accade nella stessa discussione.)

Iteratori ed elenchi di raccolte possono lanciare ConcurrentModificationException in questo caso, ma non è necessario. Quelli che mostrano il comportamento fallito. Apparentemente, l'enumerazione di Vector non è fail-fast.

La sicurezza del thread implica ovviamente più thread in qualche modo. Vector è thread-safe solo nel senso che le sue operazioni (come add, get, ecc.) Sono sincronizzate. Questo per evitare comportamenti non deterministici quando un thread sta aggiungendo un elemento mentre allo stesso tempo un altro thread sta cercando di rimuoverne uno ad esempio.

Ora, quando un thread è strutturalmente modificando la vostra collezione mentre un altro thread sta iterando su di esso, si ha a che fare sia con filo di sicurezza e le questioni di modifica concomitanti. In questo caso, e probabilmente in generale, è più sicuro non affidarsi al lancio di ConcurrentModificationException. È preferibile scegliere l'implementazione della raccolta appropriata (ad esempio una sicura per i thread) ed evitare/disabilitare la modifica simultanea.

Alcuni iteratori consentono di aggiungere/impostare/rimuovere elementi tramite l'iteratore stesso. Questa può essere una buona alternativa se hai davvero bisogno di modifiche simultanee.

1

Risposta breve: è un bonus feature che è stato inventato dopo che l'enumerazione era già presente, quindi il fatto che l'enumeratore non lo lanci non suggerisce nulla di particolare.

Risposta lunga:
Da wikipedia: implementazioni

Collection in pre-JDK 1.2 [...] non conteneva un quadro collezioni. I metodi standard per il raggruppamento degli oggetti Java erano tramite le classi array, Vector e Hashtable, che purtroppo non erano facili da estendere e non implementavano un'interfaccia membro standard . Per rispondere alla necessità di strutture dati di raccolta riutilizzabili [...] Il framework di collezioni è stato progettato e sviluppato principalmente da Joshua Bloch ed è stato introdotto in JDK 1.2.

Quando la squadra Bloch ha fatto, hanno pensato che fosse una buona idea di mettere in un nuovo meccanismo che emette un allarme (ConcurrentModificationException) quando le loro collezioni non sono stati sincronizzati correttamente in un programma multi-threaded. Ci sono due cose importanti da notare su questo meccanismo: 1) non è garantito il rilevamento di bug di concorrenza - l'eccezione viene lanciata solo se si è fortunati. 2) L'eccezione viene generata anche se si utilizza in modo errato la raccolta utilizzando un singolo thread (come nell'esempio).

Quindi, una raccolta che non genera uno ConcurrentModificationException quando si accede da più thread non implica che sia thread-safe.

0

Dipende da come si ottiene l'enumerazione. Vedere il seguente esempio, lo fa gettare 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()); 
     } 
    } 
} 

enumerazione è solo un'interfaccia, è il comportamento effettivo dipende dall'implementazione. L'enumerazione dalla chiamata Collections.enumeration() racchiude l'iteratore in qualche modo, quindi è davvero veloce, ma l'enumerazione ottenuta dal chiamare Vector.elements() non lo è.

L'enumerazione non-fail-veloce potrebbe introdurre arbitrario comportamento, non deterministico in un momento indeterminato nel futuro. Ad esempio: se si scrive il metodo principale come questo, verrà lanciata java.util.NoSuchElementException dopo la prima iterazione.

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); 
     }  
    } 
Problemi correlati