[Java]并发修改异常;迭代器不';行不通
在编写一个简单的游戏时,我遇到了一个并发修改异常,所以我看了这里,找到了两种不同的方法来修复它。它起作用了,但是,由于未知的原因,只有当玩家调用该函数时,AI玩家调用(相同)函数时才起作用 该函数的1.0版如下所示:[Java]并发修改异常;迭代器不';行不通,java,Java,在编写一个简单的游戏时,我遇到了一个并发修改异常,所以我看了这里,找到了两种不同的方法来修复它。它起作用了,但是,由于未知的原因,只有当玩家调用该函数时,AI玩家调用(相同)函数时才起作用 该函数的1.0版如下所示: public void eat(ArrayList<Enemy> enemys) { ArrayList<Enemy> toRemove = new ArrayList<Enemy>(); for(Enemy enemy : en
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);
}
堆栈跟踪将异常的位置放在方法
runGame()
中Main.java
的第59行。这一行显然是循环的一部分,该循环对一个集合进行迭代,该集合不是通过控制迭代器进行修改的。如果从该循环中注释掉对eat()
的调用可以解决问题,那么传递给该方法的参数必须引用正在迭代的同一个集合。所以不要那样做
eat()
的第二个代码有更好的形式,但它没有解决这个问题,这与调用树上某个地方的迭代有关
您应该如何修复它取决于您想要的行为。特别是,如果您想避免被吃掉的敌人有机会吃掉其他敌人(正如您当前的代码所尝试的那样),那么您需要更复杂的东西。另一方面,如果你想让每一个敌人都有机会,即使它自己在游戏时钟的这个滴答声中被吃掉,那么你可以使用敌人
集合的副本,可能是这样的:
public void runGame() {
List<Enemy> enemysCopy = new ArrayList<>(enemys);
for(Enemy enemy : enemys) {
enemy.eat(enemysCopy);
}
enemys.retainAll(enemysCopy);
player.eat(enemys);
player.update();
}
public void runGame(){
List-enemysCopy=new-ArrayList(enemys);
为(敌人:敌人){
敌人。吃(敌人抄袭);
}
敌人保留(敌人副本);
玩家。吃(敌人);
player.update();
}
这就是问题所在:
for(Enemy enemy : enemys) {
enemy.eat(enemys);
enemy.update();
}
eat改变了你正在迭代的敌人。一个可能的解决方案
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();
}
公共列表eat(ArrayList敌人){
ArrayList toRemove=新建ArrayList();
为(敌人:敌人){
if(敌方.location.x!=location.x&&敌方.location.y!=location.y){//检查自身
如果(与(敌人)碰撞){
如果(宽度>敌人宽度){
宽度+=宽度;
高度=宽度;
移动。添加(敌人);
}
}
}
}
返乡搬迁;
}
public void runGame(){
为(敌人:敌人){
列表吃掉=敌人吃掉(敌人);
更新();
}
敌人移除(吃掉);
玩家。吃(敌人);
player.update();
}
您是否使用多个线程?@assylas当前,您如何将错误归因于方法eat()
,但该方法没有出现在堆栈跟踪中?您的异常似乎出现在runGame()
方法中。你能在这里发布吗?问题不在eat()
方法本身,而是在围绕它的for
循环中。由于eat
更改了敌人
列表,因此for
循环使用的内部迭代器无效,从而导致异常。
public void runGame() {
List<Enemy> enemysCopy = new ArrayList<>(enemys);
for(Enemy enemy : enemys) {
enemy.eat(enemysCopy);
}
enemys.retainAll(enemysCopy);
player.eat(enemys);
player.update();
}
for(Enemy enemy : enemys) {
enemy.eat(enemys);
enemy.update();
}
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();
}