[Java]并发修改例外;迭代器不起作用

时间:2015-06-12 16:16:46

标签: java

在编写一个简单的游戏时,我遇到了并发修改异常,所以我看了一下,发现了两种不同的修复方法。它起作用了,但是,由于未知原因,只有当玩家调用该函数而不是AI玩家调用(相同)函数时。

该函数的1.0版看起来像这样:

public void eat(ArrayList<Enemy> enemys) {
    ArrayList<Enemy> toRemove = new ArrayList<Enemy>();
    for(Enemy enemy : enemys) {
        if(enemy.location.x != location.x && enemy.location.y != location.y) { //check for self
            if(collidesWith(enemy)) {
                if(width > enemy.width) {
                    width += enemy.width;
                    height = width;
                    toRemove.add(enemy);
                }
            }
        }
    }

    enemys.removeAll(toRemove);
}

因为这不起作用,我尝试使用迭代器,不幸的是,它产生了完全相同的错误:

public void eat(ArrayList<Enemy> enemys) {
    for(Iterator<Enemy> iterator = enemys.iterator(); iterator.hasNext();) {
        Enemy enemy = iterator.next();
        if(enemy.location.x != location.x && enemy.location.y != location.y) { //check for self
            if(collidesWith(enemy)) {
                if(width > enemy.width) {
                    width += enemy.width;
                    height = width;
                    iterator.remove(); //remove the enemy
                }
            }
        }
    }
}

错误消息是:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at Main.runGame(Main.java:59)
at Main.<init>(Main.java:43)
at Main.main(Main.java:68)

提前致谢

-v0xelDev

编辑:正如Abishek Manoharan要求runGame()方法,这里是:

public void runGame() {
    for(Enemy enemy : enemys) {
        enemy.eat(enemys);
        enemy.update();
    }
    player.eat(enemys);
    player.update();
}

3 个答案:

答案 0 :(得分:1)

这是问题所在:

for(Enemy enemy : enemys) {
    enemy.eat(enemys);
    enemy.update();
}

吃掉会改变你正在迭代的敌人。

答案 1 :(得分:1)

可能的解决办法......

public List eat(ArrayList<Enemy> enemys) {
    ArrayList<Enemy> toRemove = new ArrayList<Enemy>();
    for(Enemy enemy : enemys) {
        if(enemy.location.x != location.x && enemy.location.y != location.y) { //check for self
            if(collidesWith(enemy)) {
                if(width > enemy.width) {
                    width += enemy.width;
                    height = width;
                    toRemove.add(enemy);
                }
            }
        }
    }

    return toRemove;
}

public void runGame() {
    for(Enemy enemy : enemys) {
        List eaten = enemy.eat(enemys);
        enemy.update();
    }
    enemys.removeAll(eaten);
    player.eat(enemys);
    player.update();
}

答案 2 :(得分:0)

堆栈跟踪在方法Main.java中将异常的位置放在runGame()的第59行。这一行显然是循环的一部分,循环遍历一个被修改而不是通过控制Iterator的集合。如果从该循环中注释掉eat()的调用可以解决问题,则必须是传递给该方法的参数引用正在迭代的同一个集合。所以不要这样做。

eat()的第二个代码有更好的形式,但它没有解决问题,这与调用树中某个地方的迭代有关。

如何修复它取决于您想要的行为。特别是,如果你想避免因为有机会吃掉其他敌人而被吃掉的enemy(正如你现在的代码所做的那样),那么你需要更复杂的东西。另一方面,如果你希望每个敌人都有机会,即使它本身就在游戏时钟的这个刻度上被吃掉,那么你可以使用enemys集合的副本,也许是这样:< / p>

public void runGame() {
    List<Enemy> enemysCopy = new ArrayList<>(enemys);

    for(Enemy enemy : enemys) {
        enemy.eat(enemysCopy);
    }
    enemys.retainAll(enemysCopy);
    player.eat(enemys);
    player.update();
}