简体   繁体   English

[Java]并发修改异常; 迭代器不起作用

[英][Java]Concurrent Modification Exception; Iterator doesn't work

While programming a simple game I came accross a concurrent modification exception, so I looked here, and found two different ways to fix it. 在编写一个简单的游戏时,我遇到了一个并发的修改异常,因此我在这里看了一下,发现了两种解决方法。 It worked, but, for unknown reasons, only when the player calls the function and not if AI players call (the same) function. 它起作用了,但是由于未知的原因,只有当玩家调用函数时才起作用,而如果AI玩家调用(相同)函数则不起作用。

Version 1.0 of the function looked like this: 该功能的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);
}

as this didn't work, I tried the trick with the Iterator which, unfortunately, produces exactly the same error: 因为这不起作用,所以我尝试了使用Iterator的技巧,不幸的是,它产生了完全相同的错误:

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
                }
            }
        }
    }
}

The error message is: 错误消息是:

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)

thanks in advance 提前致谢

-v0xelDev -v0xelDev

EDIT: as Abishek Manoharan asked for the runGame() Method, here it is: 编辑:正如Abishek Manoharan要求使用runGame()方法,这里是:

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

This is the problem: 这就是问题:

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

The eat alters the enemys you are iterating through with for. 吃饭改变了你反复尝试的敌人。

A possible fix... 可能的解决方法...

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

The stack trace places the location of the exception at line 59 of Main.java , in method runGame() . 堆栈跟踪将异常的位置放置在Main.java第59行的runGame()方法中。 This line is apparently part of a loop that iterates over a collection that was modified other than via the controlling Iterator . 该行显然是循环的一部分,该循环遍历除通过控制Iterator之外的其他方式修改的集合。 If commenting out an invocation of eat() from that loop solves the problem, then it must be that the argument passed to that method refers to the same collection that is being iterated. 如果注释掉该循环中的eat()调用可以解决问题,那么必须是传递给该方法的参数引用了要迭代的同一集合。 So don't do that. 所以不要那样做。

Your second code for eat() has better form, but it doesn't address the problem, which is related to an iteration somewhere further up the call tree. 您的eat()第二个代码具有更好的形式,但没有解决问题,这与调用树上更远处的迭代有关。

How you should fix it depends on the behavior you want. 您应该如何解决它取决于您想要的行为。 In particular, if you want to avoid enemy s that are eaten from having the opportunity to eat other enemies (as your current code attempts to do), then you'll need something more sophisticated. 特别是,如果您想避免被吃掉的enemy有机会吃掉其他敌人(正如您当前的代码所尝试的那样),那么您将需要更复杂的东西。 On the other hand, if you want every enemy to get its chance, even if it is itself eaten at this tick of the game clock, then you can work with a copy of the enemys collection, maybe like so: 另一方面,如果您希望每个敌人都获得机会,即使它本身在游戏时钟的这个滴答声中被吃掉了,那么您也可以使用enemys收集的副本,也许是这样:

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM