Параллельная модификация Исключение: добавление в ArrayList
Проблема возникает при
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)
Как я могу сделать эту работу?
Ответы
Ответ 1
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 );
Также вы должны рассмотреть усиление для каждого цикла, как предложил Джон.
Ответ 2
Обычно я использую что-то вроде этого:
for (Element element : new ArrayList<Element>(mElements)) {
...
}
быстрый, чистый и без ошибок
другой вариант - использовать CopyOnWriteArrayList
Ответ 3
Вам не разрешается добавлять запись в коллекцию во время ее повторения.
Один из вариантов состоит в том, чтобы создать новый List<Element>
для новых записей, пока вы повторяете mElements
, а затем добавьте все новые в mElement
после этого (mElements.addAll(newElements)
). Конечно, это означает, что вы не выполнили тело цикла для этих новых элементов - это проблема?
В то же время, я бы рекомендовал вам обновить код, чтобы использовать для цикла:
for (Element element : mElements) {
...
}
Ответ 4
Также должен работать индексированный цикл.
for (int i = 0; i < collection.size(); i++)
Ответ 5
добавление из списка в этом случае приводит к 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 на наилучший результат...
Ответ 6
Использование итераторов также устраняет проблемы concurrency, например:
Iterator<Object> it = iterator.next().iterator();
while (it.hasNext()) {
it.remove();
}
Ответ 7
Ну, я пробовал все аспекты в моем случае, когда я выполнял итерацию в списке адаптеров, но из-за удара снова и снова я показал мне сообщение об исключении.
Я попробовал листинг списка
= (CopyOnWriteArraylist<MyClass>)mylist.value;
но это также избавило меня от исключения CouldNotCastException (и я, наконец, задумался над тем, почему они используют или предоставляют нам возможность кастинга).
Я даже использовал так называемый Синхронизированный блок, но даже он не работал или я мог бы использовать его неправильно.
Таким образом, все это, когда я наконец использовал #all времени # Техника обработки исключения
в try catch block, и это сработало
Поэтому поставьте свой код в
try{
//block
}catch(ConcurrentModificationException){
//thus handling my code over here
}
Ответ 8
Вы можете использовать цикл for
с автоматическим декрементом и в следующий раз разобраться с дополнительными элементами.
List additionalElements = new ArrayList();
for(int i = mElements.size() - 1; i > -1 ; i--){
//your business
additionalElements.add(newElement);
}
mElements.add(additionalElements);
Ответ 9
Я решил создать замок (Kotlin):
import java.util.concurrent.locks.ReentrantLock
Class A {
private val listLock = ReentrantLock()
fun doSomething(newElement){
listLock.lock()
list.add(newElement)
listLock.unlock()
}
}