it-swarm.com.de

Ausnahme für gleichzeitige Änderung: Hinzufügen zu einer ArrayList

Das Problem tritt bei auf 

Element element = it.next();

Und dieser Code, der diese Zeile enthält, befindet sich in einem OnTouchEvent

for (Iterator<Element> it = mElements.iterator(); it.hasNext();){
    Element element = it.next();

    if(touchX > element.mX  && touchX < element.mX + element.mBitmap.getWidth() && touchY > element.mY   
            && touchY < element.mY + element.mBitmap.getHeight()) {  

        //irrelevant stuff..

        if(element.cFlag){
            mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
            element.cFlag = false;

        }           
    }
}

Dies alles befindet sich in synchronized(mElements), wobei mElements ein ArrayList<Element> ist. 

Wenn ich ein Element berühre, kann es cFlag aktivieren, wodurch ein anderes Element mit anderen Eigenschaften erstellt wird, das vom Bildschirm herunterfällt und sich in weniger als einer Sekunde zerstört. Es ist meine Art, Partikeleffekte zu erzeugen. Wir können dieses "Teilchen" crack wie den String-Parameter im Konstruktor nennen.

Alles funktioniert gut, bis ich ein weiteres Haupt Element hinzufüge. Jetzt habe ich zwei Elements gleichzeitig auf dem Bildschirm, und wenn ich das neueste Element anfasse, funktioniert es einwandfrei und die Partikel werden gestartet.

Wenn ich jedoch cFlag auf dem älteren Element berühre und aktiviere, wird mir die Ausnahme angezeigt.

 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): FATAL EXCEPTION: main
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): Java.util.ConcurrentModificationException
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at Java.util.ArrayList$ArrayListIterator.next(ArrayList.Java:573)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.Juggle2.Panel.onTouchEvent(Panel.Java:823)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at Android.view.View.dispatchTouchEvent(View.Java:3766)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at Android.view.ViewGroup.dispatchTouchEvent(ViewGroup.Java:863)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at Android.view.ViewGroup.dispatchTouchEvent(ViewGroup.Java:863)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.Android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.Java:1767)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.Android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.Java:1119)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at Android.app.Activity.dispatchTouchEvent(Activity.Java:2086)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.Android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.Java:1751)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at Android.view.ViewRoot.handleMessage(ViewRoot.Java:1785)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at Android.os.Handler.dispatchMessage(Handler.Java:99)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at Android.os.Looper.loop(Looper.Java:123)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at Android.app.ActivityThread.main(ActivityThread.Java:4627)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at Java.lang.reflect.Method.invokeNative(Native Method)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at Java.lang.reflect.Method.invoke(Method.Java:521)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:893)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:651)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at dalvik.system.NativeStart.main(Native Method)

Wie kann ich diese Arbeit machen?

39
Houseman

ConcurrentModificationException tritt auf, wenn Sie die Liste ändern (indem Sie Elemente hinzufügen oder entfernen), während Sie eine Liste mit Iterator durchlaufen.

Versuchen 

List<Element> thingsToBeAdd = new ArrayList<Element>();
for(Iterator<Element> it = mElements.iterator(); it.hasNext();) {
    Element element = it.next();
    if(...) {  
        //irrelevant stuff..
        if(element.cFlag){
            // mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
            thingsToBeAdd.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
            element.cFlag = false;
        }           
    }
}
mElements.addAll(thingsToBeAdd );

Sie sollten auch für jede Schleife die von Jon empfohlene Verbesserung berücksichtigen.

60
user802421

Ich benutze normalerweise so etwas:

for (Element element : new ArrayList<Element>(mElements)) {
    ...
}

schnell, sauber und fehlerfrei

eine andere Option ist die Verwendung von CopyOnWriteArrayList

37
konmik

Sie können keinen Eintrag zu einer Sammlung hinzufügen, während Sie darüber iterieren.

Eine Möglichkeit ist, einen neuen List<Element> für neue Einträge zu erstellen, während Sie mElements durchlaufen, und anschließend alle neuen zu mElement hinzufügen (mElements.addAll(newElements)). Das bedeutet natürlich, dass Sie den Schleifenkörper nicht für diese neuen Elemente ausgeführt haben. Ist das ein Problem?

Gleichzeitig würde ich empfehlen, dass Sie Ihren Code aktualisieren, um die enhanced for-Schleife zu verwenden:

for (Element element : mElements) {
    ...
}
20
Jon Skeet

Eine indizierte for-Schleife sollte ebenfalls funktionieren.

for (int i = 0; i < collection.size(); i++)
10
Ixx

hinzufügen von der Liste führt in diesem Fall zu CME, keine Menge von synchronized lässt Sie dies vermeiden. Erwägen Sie stattdessen das Hinzufügen mit dem Iterator ...

        for(ListIterator<Element> it = mElements.listIterator(); it.hasNext();){
            Element element = it.next();

            if(touchX > element.mX  && touchX < element.mX + element.mBitmap.getWidth() && touchY > element.mY   
                    && touchY < element.mY + element.mBitmap.getHeight()) {  

                //irrelevant stuff..

                if(element.cFlag){
                    // mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
                    it.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
                    element.cFlag = false;

                }           
            }
        }

Außerdem finde ich es etwas rutschig zu sagen wie ...

... Das Problem tritt bei Element element = it.next(); auf

aus Gründen der Genauigkeit wird darauf hingewiesen, dass oben nicht garantiert wird.

API-Dokumentation weist darauf hin, dass dieses ... Verhalten nicht garantiert werden kann, da es im Allgemeinen unmöglich ist, bei nicht synchronisierten gleichzeitigen Änderungen eine harte Garantie zu geben. Fehlgeschlagene Operationen lösen eine ConcurrentModificationException auf bestmöglicher Basis aus ...

2
gnat

Sie könnten eine automatische Dekrementierungsschleife for verwenden und sich das nächste Mal mit den zusätzlichen Elementen befassen.

List additionalElements = new ArrayList();
for(int i = mElements.size() - 1; i > -1 ; i--){
    //your business
    additionalElements.add(newElement);
}
mElements.add(additionalElements);
1
Saren

Mit Iteratoren werden auch Parallelitätsprobleme wie die folgenden behoben:

Iterator<Object> it = iterator.next().iterator();
while (it.hasNext()) {
    it.remove();
}
1
David Bemerguy

Nun, ich habe alle Aspekte in meinem Fall ausprobiert, bei denen ich in einem Adapter eine Liste durchlaufen habe, aber wegen immer wiederem Schlagen zeigte ich mir die Meldung, dass eine Ausnahme ausgelöst wurde ... Ich habe versucht, die Liste zu gießen

 = (CopyOnWriteArraylist<MyClass>)mylist.value;

aber es wurde mir auch eine Ausnahme von CouldNotCastException geworfen (und ich überlegte schließlich, dass sie uns eine Facalität beim Casting zukommen lassen).

Ich habe sogar den sogenannten Synchronized Block verwendet, aber selbst der hat nicht funktioniert oder ich hätte ihn falsch verwendet.

Es ist also alles, als ich endlich die #all of time # - Technik der Ausnahmebehandlung verwendeteIn try catch block, und es hat funktioniertSo fügen Sie Ihren Code in die

try{
//block

}catch(ConcurrentModificationException){
//thus handling my code over here
}

Ich habe das Erstellen eines Schlosses (Kotlin) gelöst:

import Java.util.concurrent.locks.ReentrantLock

Class A {
    private val listLock = ReentrantLock()
    fun doSomething(newElement){
        listLock.lock()
        list.add(newElement)
        listLock.unlock()
    }
}
0
Marco