Wyjątek modyfikacji współbieżnej: dodawanie do ArrayList

Problem występuje w

Element element = it.next();

I ten kod, który zawiera ten wiersz, jest wewnątrz 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;

        }           
    }
}

Wszystko to jest wewnątrz synchronized(mElements), Gdzie mElements jest ArrayList<Element>

Kiedy dotknę Element, może aktywować cFlag, co utworzy kolejną Element o różnych właściwościach, która spadnie z ekranu i zniszczy się w mniej niż sekundę. To mój sposób na tworzenie efektów cząsteczkowych. Możemy nazwać tę "cząstkę" crack, podobnie jak parametr String w konstruktor.

To wszystko działa dobrze, dopóki nie dodam kolejnego głównego Element. Teraz mam dwa Elements na ekranie w tym samym czasie, i jeśli dotknę najnowszego Element, działa dobrze i uruchamia cząstki.

Jednak, jeśli dotknę i aktywuję cFlag na starszym Element, to daje mi wyjątek.

 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)

Jak mogę to zrobić?

Author: Nicolas Filotto, 2011-07-29

7 answers

ConcurrentModificationException występuje podczas modyfikowania listy (poprzez dodawanie lub usuwanie elementów) podczas przechodzenia listy za pomocą Iterator.

Try

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

Należy również rozważyć enhanced dla każdej pętli, jak zasugerował Jon.

 53
Author: user802421,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-28 22:02:29

Normalnie używam czegoś takiego:

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

Szybko, czysto i bez błędów

Inną opcją jest użycie CopyOnWriteArrayList

 36
Author: konmik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-20 09:44:34

Nie możesz dodawać wpisu do kolekcji, gdy nad nią pracujesz.

Jedną z opcji jest utworzenie nowego List<Element> dla nowych wpisów podczas iteracji mElements, a następnie dodanie wszystkich nowych do mElement (mElements.addAll(newElements)). Oczywiście oznacza to, że nie wykonasz ciała pętli dla tych nowych elementów - czy to problem?

W tym samym czasie, polecam, aby zaktualizować swój kod, aby używać enhanced for loop :

for (Element element : mElements) {
    ...
}
 18
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-28 21:56:49

Pętla indeksowana for powinna również działać.

for (int i = 0; i < collection.size(); i++)
 10
Author: Ixx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-28 13:01:54

Dodanie z listy w tym przypadku prowadzi do CME, żadna ilość synchronized nie pozwoli Ci tego uniknąć. Zamiast tego rozważ dodanie za pomocą iteratora...

        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;

                }           
            }
        }

Również myślę, że to trochęśliskie aby stwierdzić jak...

...Problem występuje w Element element = it.next();

Ze względu na precyzję należy pamiętać, że powyższe nie jest gwarantowane.

Dokumentacja API wskazuje, że to ...zachowanie nie może być zagwarantowane tak jak jest, ogólnie rzecz biorąc, niemożliwe jest dokonanie jakichkolwiek twardych gwarancji w obecności niezsynchronizowanej jednoczesnej modyfikacji. Operacje bezawaryjne rzucają ConcurrentModificationException na podstawie najlepszego wysiłku...

 2
Author: gnat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-29 00:17:16

Używanie iteratorów rozwiązuje również problemy ze współbieżnością, takie jak:

Iterator<Object> it = iterator.next().iterator();
while (it.hasNext()) {
    it.remove();
}
 1
Author: David Bemerguy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-24 04:17:10

Cóż, próbowałem wszystkich aspektów w moim przypadku, w którym iterację w adapterze listy, ale z powodu uderzania raz po raz pokazałem mi wiadomość o wyjątku wyrzucany . Próbowałem wrzucić listę do

 = (CopyOnWriteArraylist<MyClass>)mylist.value;

Ale to również rzuciło mi wyjątek od CouldNotCastException, (i w końcu zastanowiłem się nad tym, dlaczego używają lub zapewniają nam facality castingu).

Użyłem nawet tzw Synchronized Block zbyt, ale nawet to nie działa lub mógłbym mieć używałem go w niewłaściwy sposób.

Więc to wszystko, kiedy w końcu użyłem #all of time # techniki obsługi wyjątku w try catch block, I to działało Więc wpisz swój kod w

try{
//block

}catch(ConcurrentModificationException){
//thus handling my code over here
}
 1
Author: Siddharth Choudhary,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-21 06:53:29