Abnormales Verhalten von java.util.List basierend auf der Anzahl der darin enthaltenen Elemente

8

Ich weiß, dass die iterator.next (), wenn die Collection geändert wird, während ein Thread sie mit Iterator durchläuft, ein SimultaneousModificationException .

Aber es zeigt ein anderes Verhalten, abhängig von der Anzahl der Elemente in der Liste.

Ich habe ein Code-Snippet ausprobiert, in dem ich eine Liste in for-jeder Schleife durchlaufen habe und dazwischen ein traversal mit der Methode remove () der Liste ein Element aus der Liste entfernt habe.

Idealerweise sollte es eine ConcurrentModificationException in dieser Bedingung auslösen, ohne von der Anzahl der Elemente in der Liste abhängig zu sein. Dies trifft jedoch nicht zu, wenn die Anzahl der Elemente in der Liste zwei ist.

Fall 1: Anzahl der Elemente in der Liste - 1

%Vor%
  

Ausgabe : Eins

     

Ausnahme im Thread "main" java.util.ConcurrentModificationException

Das war wie erwartet.

Fall 2: Anzahl der Elemente in der Liste - 2

%Vor%
  

Ausgabe: Eins

Es wird keine Ausnahme ausgelöst ?????????

Fall 3: Anzahl der Elemente in der Liste - 3

%Vor%
  

Ausgabe : Eins

     

Ausnahme im Thread "main" java.util.ConcurrentModificationException

Wieder wird eine Ausnahme ausgelöst, was ein ideales Verhalten ist.

Aber warum wird es ordnungsgemäß in case-2 ausgeführt, ohne dass eine ConcurrentModificationException ausgelöst wird.

    
Ankur Lathi 21.07.2013, 12:17
quelle

3 Antworten

7

Aus der Dokumentation (Hervorhebung von mir) :

  

Die Iteratoren, die von der Klasse iterator und listIterator zurückgegeben wurden   Methoden sind fail-fast : wenn die Liste strukturell modifiziert ist   Zeit, nachdem der Iterator erstellt wurde, in keiner Weise außer durch   Iterator eigene remove oder add -Methoden, der Iterator wirft ein   %Code%. So angesichts der gleichzeitigen   Änderung, der Iterator schlägt schnell und sauber, anstatt   riskieren willkürliches, nicht-deterministisches Verhalten zu einer unbestimmten Zeit   in der Zukunft.

     

Beachten Sie, dass das Fail-Fast-Verhalten eines Iterators nicht garantiert werden kann   wie es im allgemeinen unmöglich ist, harte Garantien zu geben   bei unsynchronisierter gleichzeitiger Änderung. Fail-schnell   Iteratoren werfen ConcurrentModificationException auf Best-Effort   Basis. Daher wäre es falsch, ein Programm zu schreiben, das davon abhing   auf diese Ausnahme für ihre Richtigkeit: das Fail-Fast-Verhalten von   Iteratoren sollten nur verwendet werden, um Bugs zu erkennen .

    
abacabadabacaba 21.07.2013, 12:26
quelle
7

Die vorherigen Antworten zeigen Ihnen die relevanten Dokumente, die erklären, warum dies angemessen ist. Sie sind nicht garantiert , um diese Ausnahme zu erhalten.

Wenn Sie wirklich wissen wollen, warum Sie nicht mit nur zwei Elementen erhalten (oder wirklich, wenn Sie das vorletzte Element unabhängig von der Größe entfernen), können Sie nachsehen im Quellcode für ArrayList .

Ihre Schleife:

%Vor%

ist eigentlich:

%Vor%

Intern in Arraylist gibt es ein int size , das die aktuelle Anzahl der Elemente in ArrayList darstellt. Es wird dekrementiert, wenn Sie remove() aufrufen.

Innerhalb der Iterator gibt es eine int cursor , die den aktuellen Standort (Index) von Iterator darstellt. Es wird erhöht, wenn Sie next()

aufrufen

hasNext() in Iterator prüft die aktuelle Cursorposition gegen die Größe.

In Ihrem Beispiel ist die Kette der Ereignisse wie folgt:

  • cursor beginnt bei 0 , size beginnt bei 2
  • next() wird aufgerufen, cursor wird auf 1 erhöht.
  • remove() wird aufgerufen, size wird auf 1 dekrementiert
  • hasNext() vergleicht cursor mit size , stellt fest, dass sie identisch sind, gibt false zurück
  • Schleife beendet, ohne Ausnahme auszulösen

Wenn Sie also das vorletzte Element in der Größe ArrayList entfernen, während Sie es durchlaufen, erhalten Sie keine Ausnahme. (Beachten Sie auch, dass Sie nie das letzte Element in dieser Liste in der Schleife verarbeiten werden; Ihr Beispiel druckt daher nur One aus diesem Grund).

Denken Sie jedoch daran - dies ist ein Implementierungsdetail und ist nicht garantiert. Die Dokumentation sagt Ihnen, dass Sie sich nicht darauf verlassen sollten, dass die Ausnahme ausgelöst (oder nicht ausgelöst) wird. ArrayList könnte auf eine andere Weise neu geschrieben werden, wobei das oben Genannte nicht mehr gilt und die Ausnahme ausgelöst wird (und tatsächlich in einer anderen JVM, die möglicherweise schon der Fall ist).

    
Brian Roach 21.07.2013 13:28
quelle
0

Es scheint, wenn Sie das angegebene Element aus der Liste entfernen, die Liste nicht weiß, dass ihre Größe geändert wurde.

Versuchen Sie Folgendes:

Iterator.remove ()

  

Entfernt das letzte von zurückgegebene Element aus der zugrunde liegenden Sammlung   der Iterator (optionale Operation). Diese Methode kann nur einmal aufgerufen werden   per Anruf zum nächsten. Das Verhalten eines Iterators ist nicht spezifiziert, wenn der   Die zugrunde liegende Sammlung wird während der Iteration geändert   in anderer Weise als durch Aufruf dieser Methode.

    
Achintya Jha 21.07.2013 12:24
quelle