69

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), where mElements is an ArrayList<Element>

When I touch an Element, it may activate cFlag, which will create another Element 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 two Elements on the screen at the same time, and if I touch the newest Element, it works fine, and launches the particles.

However, if I touch and activate cFlag on the older Element, 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?

1

11 Answers 11

92

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Didn't work when i had to work with remove from list, instead traditional for loop i had to go for along with modifications for "i" in loop every time.
@AmitTumkur try to use "iterator.remove();" instead of "list.remove(object)" :)
78

I normally use something like this:

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

quick, clean and bug-free

another option is to use CopyOnWriteArrayList

4 Comments

But object of new Arraylist will perform shallow copy of list, which points to same list.
No, it will not point to the same list. The list gets copied.
but wouldn´t that iterate through a list with a different count of items? I think, if you remove an item from the original list, it is still on the copied ArrayList, isn´t it?
There will be a different number of items in the modified list after modification, obviously. The point is that you will still be able to iterate over all of the original list items, while modifying the list as you wish.
24

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) {
    ...
}

3 Comments

Actually, I had the enhanced for loop at first. I just recently used an iterator because I though that would help solve the problem, but it wasn't.
I believe the enhanced for loop uses an Iterator in the background, therefore using it should cause the same problem.
@OWiz: The two forms will compile to basically the same code.
18

An indexed for loop should also work.

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

1 Comment

You could end up in an infinite loop.
3

Using Iterators also fixes concurrency problems, like this:

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

1 Comment

Iterators are only useful if you want to remove elements, but they are useless if you want to add items to a list as asked in this question.
2

adding from list in this case leads to CME, no amount of synchronized will let you avoid that. Instead, consider adding using the 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;

                }           
            }
        }

Also I think it's somewhat slippery to state like...

...The problem occurs at Element element = it.next();

for the sake of precision note that above is not guaranteed.

API documentation points out that this ...behavior cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast operations throw ConcurrentModificationException on a best-effort basis...

Comments

2

I solved creating a lock (Kotlin):

import java.util.concurrent.locks.ReentrantLock

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

Comments

2

You could use an auto-decrement for loop, and deal with the additional elements next time.

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

Comments

1

Well i have tried all the aspects in my case where i was iterating in an adapter a list but due to hitting again and again i showed me the message of exception being thrown . I tried Casting the list to

 = (CopyOnWriteArraylist<MyClass>)mylist.value;

but it also throwed me an exception of CouldNotCastException,(and i finally pondered over the fact that why do they use or provide us a facality of casting).

I even used the so called Synchronized Block too, but even it didn't worked or i might would have been using it in a wrong way.

Thus it's all when i finally used the #all of time# Technique of handling the exception in try catch block, and it worked So put your code in the

try{
//block

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

Comments

1

The accepted solution (to create a copy of the collection) usually works well.

However, if the Element contains another collection this does not make a deep copy!

Example:

class Element {
   List<Kid> kids;

   getKids() {
      return kids;
   }
}

Now when you create a copy of the List of Elements:

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

You can still get a ConcurrentModificationException if you iterate over element.getKids() and, parally, alter the kids of that element.

Looking back it's obvious, but I ended up in this thread so maybe this hint helps others, too:

class Element {
   List<Kid> kids;

   getKids() {
      // Return a copy of the child collection
      return new ArrayList<Kid>(kids);
   }
}

Comments

0
    LinkedList<Double> newList = new LinkedList<>();
    newList.addAll(oldList);

This will take care of ConcurrentModificationException and good practice

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.