Исключение одновременной модификации: добавление в ArrayList - PullRequest
42 голосов
/ 29 июля 2011

Проблема возникает на

Element element = it.next();

И этот код, который содержит эту строку, находится внутри 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;

        }           
    }
}

Все это внутри synchronized(mElements), где mElements является ArrayList<Element>

Когда я касаюсь Element, он может активировать cFlag, что создаст еще один Element с другими свойствами, который упадет с экрана и уничтожит себя менее чем за секунду. Это мой способ создания эффектов частиц. Мы можем назвать эту «частицу» crack, как параметр String в конструкторе.

Это все работает нормально, пока я не добавлю еще один основной Element. Теперь у меня есть два Elements на экране одновременно, и если я коснусь новейшего Element, он будет работать нормально и запустит частицы.

Однако, если я коснусь и активирую cFlag на более старом Element, это даст мне исключение.

 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)

Как я могу сделать эту работу?

Ответы [ 7 ]

64 голосов
/ 29 июля 2011

ConcurrentModificationException возникает при изменении списка (путем добавления или удаления элементов) при обходе списка с помощью Iterator.

Попробуйте

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

Также вам следует подумать об улучшении для каждого цикла, как предложил Джон.

44 голосов
/ 20 января 2015

Я обычно использую что-то вроде этого:

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

быстро, чисто и без ошибок

другой вариант - использовать CopyOnWriteArrayList

20 голосов
/ 29 июля 2011

Вам не разрешено добавлять запись в коллекцию во время итерации по ней.

Один из вариантов - создать новую List<Element> для новых записей, пока вы повторяете по mElements, а затем добавьте все новые к mElement (mElements.addAll(newElements)).Конечно, это означает, что вы не выполнили тело цикла для этих новых элементов - это проблема?

В то же время, я бы порекомендовал обновить ваш код для использования улучшено для цикла :

for (Element element : mElements) {
    ...
}
12 голосов
/ 17 мая 2012

Индексированный цикл также должен работать.

for (int i = 0; i < collection.size(); i++)
2 голосов
/ 29 июля 2011

добавление из списка в этом случае приводит к CME, никакое количество synchronized не позволит вам этого избежать.Вместо этого рассмотрите возможность добавления с помощью итератора ...

        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;

                }           
            }
        }

Также я думаю, что это несколько скользко , чтобы заявить как ...

..Проблема возникает в Element element = it.next();

ради точности, отметьте, что приведенное выше не гарантируется.

Документация API указывает, что это ... поведение не может быть гарантировано, так как, вообще говоря, невозможно сделать какие-либо жесткие гарантии при наличии несинхронизированной параллельной модификации.Отказоустойчивые операции генерируют исключение ConcurrentModificationException на максимальных усилиях ...

1 голос
/ 21 июля 2017

Ну, я попробовал все аспекты в моем случае, когда я перебирал в адаптере список, но из-за повторного нажатия снова и снова я показывал мне сообщение об исключении. Я попытался привести список к

 = (CopyOnWriteArraylist<MyClass>)mylist.value;

но это также бросило мне исключение CouldNotCastException, (и я, наконец, задумался над тем, почему они используют или предоставляют нам возможность кастинга).

Я даже использовал так называемый Синхронизированный блок, но даже он не работал, или я мог бы использовать его неправильно.

Таким образом, все, когда я наконец-то использовал # все время # Техника обработки исключения в блоке try catch, и это сработало Поэтому поместите свой код в

try{
//block

}catch(ConcurrentModificationException){
//thus handling my code over here
}
1 голос
/ 29 сентября 2014

Использование итераторов также устраняет проблемы параллелизма, например:

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