Search code examples
javaiteratorconcurrentmodification

How to avoid the ConcurrentModificationException


I am trying to remove items from an array list with an iterator and I keep getting the ConcurrentModificationExceptionhere is my code:

public void forward() 
{
    for (Sprite s : sprites)
    {
        s.move();
        for (Iterator<Sprite> iter = sprites.iterator(); iter.hasNext();) 
        {  
            s = iter.next();
            if (s instanceof Attacker)
            {
                for (Sprite s2  : sprites)
                {
                    if(s.overlaps(s2))
                        s2.hit();
                }
            }
            if (s.shouldRemove())
                iter.remove();
        }
    }
}

it works for about the first 15 to 20 times and then I get the error every couple clicks

Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at Model.forward(Model.java:46)
at Controller.mousePressed(Controller.java:29)
at java.awt.Component.processMouseEvent(Component.java:6522)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
at java.awt.Component.processEvent(Component.java:6290)
at java.awt.Container.processEvent(Container.java:2234)
at java.awt.Component.dispatchEventImpl(Component.java:4881)
at java.awt.Container.dispatchEventImpl(Container.java:2292)
at java.awt.Component.dispatchEvent(Component.java:4703)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4898)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4530)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4462)
at java.awt.Container.dispatchEventImpl(Container.java:2278)
at java.awt.Window.dispatchEventImpl(Window.java:2739)
at java.awt.Component.dispatchEvent(Component.java:4703)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:746)
at java.awt.EventQueue.access$400(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:697)
at java.awt.EventQueue$3.run(EventQueue.java:691)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.awt.EventQueue$4.run(EventQueue.java:719)
at java.awt.EventQueue$4.run(EventQueue.java:717)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:716)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

I am not entirely sure which one throws the errors


Solution

  • Your get a ConcurrentModificationException because you remove an element from a collection while you are iterating over that collection, other than via the iterator. In this case, you have no explicit iterator for the outer iteration, so there is no way to safely modify the sprites collection inside the loop.

    The best you can do is probably to collect the elements to delete into a temporary collection, then remove them all after the loop, something like this:

    Set<Sprite> toRemove = new HashSet<Sprite>();
    
    for (Sprite s1 : sprites) {
        if (toRemove.contains(s1)) {
            continue;
        }
        s1.move();
        for (Sprite s : sprites) {
            if (toRemove.contains(s)) {
                continue;
            }
            if (s instanceof Attacker) {
                for (Sprite s2  : sprites) {
                    if (toRemove.contains(s2)) {
                        continue;
                    }
                    if(s.overlaps(s2)) {
                        s2.hit();
                    }
                }
            }
            if (s.shouldRemove()) {
                toRemove.add(s);
            }
        }
    }
    
    sprites.removeAll(toRemove);