Как избежать ConcurrentModificationException

Я пытаюсь удалить элементы из списка массивов с помощью итератора и продолжаю получать ConcurrentModificationExceptionвот мой код:

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

это работает примерно первые 15-20 раз, а затем я получаю сообщение об ошибке каждые пару кликов

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)

Я не совсем уверен, какой из них выдает ошибки


person cwendel    schedule 17.10.2014    source источник
comment
какая строка выдает ошибку?   -  person dkatzel    schedule 17.10.2014
comment
какая строка в вашем примере является строкой 46 в вашем файле (Model.forward(Model.java:_46_))?   -  person jtahlborn    schedule 17.10.2014
comment
функция удара изменяет его на другое изображение и останавливает его движение   -  person cwendel    schedule 18.10.2014


Ответы (1)


Вы получаете ConcurrentModificationException, потому что вы удаляете элемент из коллекции, когда перебираете эту коллекцию, кроме как через итератор. В этом случае у вас нет явного итератора для внешней итерации, поэтому нет способа безопасно изменить коллекцию sprites внутри цикла.

Лучшее, что вы можете сделать, это, вероятно, собрать элементы для удаления во временную коллекцию, а затем удалить их все после цикла, что-то вроде этого:

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);
person John Bollinger    schedule 17.10.2014
comment
Нет, внешний цикл (в исходном коде) — это for (Sprite s : sprites) { ... }. iter — это итератор для среднего цикла. - person John Bollinger; 17.10.2014
comment
святая корова, как я пропустил это! я прочитал этот код 10 раз и пропустил этот внешний цикл. - person jtahlborn; 17.10.2014
comment
Отложенное удаление может изменить поведение, если какой-либо из методов, вызываемых для экземпляров Sprite (move(), overlaps(Sprite), hit() и shouldRemove()), использует коллекцию sprites. Если это так, то, вероятно, все это нужно пересмотреть. - person John Bollinger; 18.10.2014
comment
хех, да, помимо CME есть некоторые проблемы в логике кода. например, хиты могут поражать объекты, расположенные раньше в списке, которые не могут быть своевременно удалены. - person jtahlborn; 18.10.2014
comment
Я не совсем уверен, что такое Set и HashSet. У вас есть идеи, как это сделать без этого? Я ценю ответ, и теперь я понимаю проблему, но ответ был немного не в моей лиге: / Идея состоит в том, чтобы удалить их после последовательного количества щелчков мышью после того, как они были первоначально поражены. Мне нужно удалить их 1 на 1 - person cwendel; 18.10.2014
comment
Set — это интерфейс, представляющий другой тип коллекции. HashSet является подходящей реализацией этого интерфейса. Вместо этого вы могли бы заменить List и ArrayList, так как кажется, что вы лучше знакомы с ними. - person John Bollinger; 18.10.2014
comment
Это сделало это. Серьезно ценю это. Это было крайне неприятно - person cwendel; 18.10.2014
comment
В любом случае, при условии, что Sprite экземпляры не просматривают и не работают с общей коллекцией спрайтов (как упоминалось ранее), предложенный мной пересмотренный код будет выполнять точно такие же операции с точно такими же спрайтами, как ваш код, кажется, указывает, что вы хотели сделайте, и он оставит коллекцию спрайтов в том состоянии, в котором вы, кажется, хотели. - person John Bollinger; 18.10.2014