2012-06-04 17 views
11

Enumeration wirft nicht ConcurrentModificationException, warum?Java Enumeration vs Iterator

Siehe unten stehenden Code.

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

    } 

} 

Es druckt nur:

 
Amit 
Pathak 
Aron 

Warum ist das ein solches Verhalten. Können wir sagen, dass Enumerator Thread-sicher ist.

Bearbeiten: Bei der Arbeit mit Iterator wirft es ConcurrentModificationException in Single-Thread-Anwendung.

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

Bitte überprüfen.

+1

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

+0

Was ist der Hintergrund dieser Frage? Erwägen Sie die Verwendung von Enumerationen anstelle von Iteratoren in einer Umgebung mit mehreren Threads? –

+0

Gibt es irgendwo mehr Code, über den wir Bescheid wissen sollten? Es gibt kein Multithreading im obigen Beispielcode. Wenn Sie also keinen bestimmten Grund haben, die Thread-Sicherheit zu beschuldigen, denke ich, dass Sie hier ein wenig abwesend sind. – posdef

Antwort

4

Enumeration lesen wirft keine ConcurrentModificationException, warum?

Da im aufgerufenen Code kein Pfad vorhanden ist, der diese Ausnahme auslöst. Edit: Ich beziehe mich auf die Implementierung von der Vector-Klasse zur Verfügung gestellt, nicht über die Enumeration-Schnittstelle im Allgemeinen.

Warum ist das ein solches Verhalten? Können wir sagen, dass Enumerator threadsicher ist?

Es ist threadsicher in einem Sinn, daß der ausgeführte Code ist richtig synchronisiert. Ich glaube jedoch nicht, dass das Ergebnis, das Ihre Schleife liefert, das ist, was Sie außer Acht lassen würden.

Der Grund für Ihre Ausgabe ist, dass das Enumeration-Objekt einen Zähler verwaltet, der nach jedem Aufruf von nextElement() inkrementiert wird. Dieser Zähler ist sich Ihres Aufrufs von remove() nicht bewusst.

+0

Mein Verständnis ist Enumeration hat Bezug auf Sammlung als Iterator hat. Sobald wir das Element aus dem Vektor entfernen, wird es in Enumeration angezeigt. Warum also druckt es 3 nicht alle 5, wenn wir zuerst drucken und dann entfernen. – amicngh

+0

Das ist der Punkt. Die Enumeration bemerkt nicht, dass Sie remove aufrufen (was es notwendig machen würde, die Indexvariable zu dekrementieren). – MartinK

-1

die v.remove(value) entfernen und alles wird als

bearbeiten erwartet funktionieren: obwohl leid, die Frage falsch verstanden es

Das hat nichts mit threadsafety zu tun hat. Sie sind nicht einmal Multithreading, also gibt es keinen Grund, warum Java dafür eine Ausnahme auslöst.

Wenn Sie Ausnahmen wünschen, wenn Sie den Vektor machen ändern es Unmodifiable

+4

Dies scheint keine Antwort auf die Frage zu sein –

+0

10 Downvote entfernt nach Bearbeitung .. –

10

Beachten Sie, dass ConcurrentModificationException nichts mit Gleichzeitigkeit im Sinne von Multithreading oder Threadsicherheit zu tun hat. Einige Sammlungen ermöglichen gleichzeitige Änderungen, andere nicht. Normalerweise finden Sie die Antwort in den Dokumenten. Aber gleichzeitig bedeutet nicht gleichzeitig verschiedene Threads. Dies bedeutet, dass Sie die Sammlung während der Iteration ändern können.

ConcurrentHashMap ist ein Sonderfall, da es explizit als thread-sicher UND editierbar definiert ist, während es iteriert wird (was meiner Meinung nach für alle thread-sicheren Sammlungen gilt).

Wie auch immer, solange Sie einen einzelnen Thread zum Iterieren und Ändern der Sammlung verwenden, ist ConcurrentHashMap die falsche Lösung für Ihr Problem. Sie verwenden die API falsch. Sie sollten Iterator.remove() verwenden, um Elemente zu entfernen. Alternativ können Sie eine Kopie der Sammlung erstellen, bevor Sie das Original iterieren und ändern.

EDIT:

Ich weiß nicht jeder Enumeration, die eine ConcurrentModificationException wirft. Das Verhalten bei einer gleichzeitigen Änderung ist jedoch möglicherweise nicht das, was Sie erwarten. Wie Sie in Ihrem Beispiel sehen, überspringt die Enumeration jedes zweite Element in der Liste. Dies liegt daran, dass der interne Index unabhängig von Löschvorgängen erhöht wird. Also das ist, was passiert:

  • en.nextElement() - liefert das erste Element von Vector, erhöht Index 1
  • v.remove (Wert) - das erste Element von Vector entfernt, verschiebt alle Elemente links
  • en.nextElement() - liefert das zweite Element von Vector, die jetzt „Pathak“

Die fehler schnell Verhalten von Iterator schützt Sie vor dieser Art von Dingen, weshalb es in der Regel vorzuziehen Enumberation ist. Stattdessen sollten Sie folgendes tun:

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

Alternativ:

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

Die erste ist sicherlich vorzuziehen, da Sie wirklich nicht die Kopie benötigen, solange Sie die API verwenden, wie es ist beabsichtigt.

+0

Endlich verstehe ich, dass Enumeration nie ConcurrentModificationException werfen? – amicngh

+1

+1 für "ConcurrentModificationException hat nichts mit Gleichzeitigkeit im Sinne von Multithreading oder Thread-Sicherheit zu tun" –

3

Die gleichzeitige Änderung hat hier nichts mit Threads zu tun.

Nebenläufigkeit hier bedeutet einfach, dass Sie die Sammlung ändern, während Sie darüber iterieren. (In Ihrem Beispiel geschieht dies im selben Thread.)

Iteratoren und Aufzählungen von Sammlungen können ConcurrentModificationException in diesem Fall werfen, müssen aber nicht. Diejenigen, die zeigen fail-fast Verhalten. Offensichtlich ist die Aufzählung von Vector nicht Fail-Fast.

Thread-Sicherheit betrifft offensichtlich mehrere Threads irgendwie. Vector ist Thread-sicher nur in dem Sinne, dass seine Operationen (wie Add, Get, etc.) synchronisiert sind. Dies dient dazu, nichtdeterministisches Verhalten zu vermeiden, wenn ein Thread ein Element hinzufügt, während gleichzeitig ein anderer Thread versucht, einen zu entfernen.

Wenn nun ein Thread Ihre Sammlung strukturell ändert, während ein anderer Thread darüber iteriert, müssen Sie sowohl mit Threadsicherheit als auch mit gleichzeitiger Änderung umgehen. In diesem Fall und wahrscheinlich im Allgemeinen ist es am sichersten, sich nicht auf ConcurrentModificationException zu verlassen. Am besten ist es, die geeignete Implementie- rung der Sammlung zu wählen (z. B. eine thread-sichere) und die gleichzeitige Änderung zu vermeiden/zu verhindern.

Einige Iteratoren erlauben das Hinzufügen/Setzen/Entfernen von Elementen durch den Iterator selbst. Dies kann eine gute Alternative sein, wenn Sie eine gleichzeitige Änderung benötigen.

1

Kurze Antwort: Es ist eine bonus feature, die nach der Aufzählung erfunden wurde, war schon da, so dass die Tatsache, dass der Enumerator es nicht wirft, nichts Besonderes vorschlägt.

Lange Antwort:
Aus Wikipedia:

Sammlung Implementierungen in pre-JDK 1.2 [...] hat keinen Sammlungen Rahmen enthalten. Die Standardmethoden zum Gruppieren von Java Objekten waren über das Array, die Vector- und die Hashtable-Klasse , die leider nicht einfach zu erweitern waren, und implementierten keine Standard-Member-Schnittstelle . Um die Notwendigkeit für wiederverwendbare Sammlung Datenstrukturen zu adressieren [...] Die Sammlungen Framework wurde in erster Linie von Joshua Bloch entworfen und entwickelt, und wurde in JDK 1.2 eingeführt.

Wenn das Bloch-Team das tat, dachte sie es eine gute Idee war, in einem neuen Mechanismus zu setzen, die einen Alarm aussendet (ConcurrentModificationException), wenn ihre Kollektionen in einem Multi-Threaded-Programm nicht richtig synchronisiert wurden. Es gibt zwei wichtige Dinge, die bei diesem Mechanismus zu beachten sind: 1) es ist nicht garantiert, dass es Nebenläufigkeitsfehler gibt - die Ausnahme wird nur ausgelöst, wenn Sie Glück haben. 2) Die Ausnahme wird auch ausgelöst, wenn Sie die Sammlung mit einem einzelnen Thread (wie in Ihrem Beispiel) missbrauchen.

Also, eine Sammlung nicht werfen ConcurrentModificationException, wenn der Zugriff durch mehrere Threads bedeutet nicht, es ist auch thread-safe.

0

Es hängt davon ab, wie Sie die Enumeration erhalten. Siehe das folgende Beispiel, es ConcurrentModificationException wirft:

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

Enumeration nur eine Schnittstelle ist, dann ist es die tatsächliche Verhalten ist abhängig von der Implementierung. Die Enumeration aus dem Aufruf Collections.enumeration() umschließt den Iterator in einiger Weise, also ist es in der Tat fehlgeschlagen, aber die Enumeration beim Aufruf von Vector.elements() ist nicht vorhanden.

Die Not-Fail-Fast-Enumeration könnte willkürliches, nicht-deterministisches Verhalten zu einem unbestimmten Zeitpunkt in der Zukunft einführen. Beispiel: Wenn Sie die main-Methode als solche schreiben, wird java.util.NoSuchElementException nach der ersten Iteration ausgelöst.

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