Concurrent Modification Exception : adding to an ArrayList
Solution 1
ConcurrentModificationException occurs when you modify the list (by adding or removing elements) while traversing a list with 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 );
Also you should consider enhanced for each loop as Jon suggested.
Solution 2
I normally use something like this:
for (Element element : new ArrayList<Element>(mElements)) {
...
}
quick, clean and bug-free
another option is to use CopyOnWriteArrayList
Solution 3
You're not allowed to add an entry to a collection while you're iterating over it.
One option is to create a new List<Element>
for new entries while you're iterating over mElements
, and then add all the new ones to mElement
afterwards (mElements.addAll(newElements)
). Of course, that means you won't have executed the loop body for those new elements - is that a problem?
At the same time, I'd recommend that you update your code to use the enhanced for loop:
for (Element element : mElements) {
...
}
Solution 4
An indexed for loop should also work.
for (int i = 0; i < collection.size(); i++)
Solution 5
Using Iterators also fixes concurrency problems, like this:
Iterator<Object> it = iterator.next().iterator();
while (it.hasNext()) {
it.remove();
}
Admin
Updated on April 08, 2020Comments
-
Admin about 4 years
The problem occurs at
Element element = it.next();
And this code which contains that line, is inside of an
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; } } }
All of this is inside
synchronized(mElements)
, wheremElements
is anArrayList<Element>
When I touch an
Element
, it may activatecFlag
, which will create anotherElement
with different properties, which will fall off the screen and destroy itself in less than a second. It's my way of creating particle effects. We can call this "particle"crack
, like the String parameter in the constructor.This all works fine until I add another main
Element
. Now I have twoElements
on the screen at the same time, and if I touch the newestElement
, it works fine, and launches the particles.However, if I touch and activate
cFlag
on the olderElement
, then it gives me the exception.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)
How can I make this work?