删除对象导致冻结

Removing an object is causing freezes

[为了澄清,'monsters' 是一个 NPC 列表,当被物体射击时 'bullet' 从列表中删除。 ]

开始时出错:

W/System.err: java.util.ConcurrentModificationException
W/System.err:     at java.util.ArrayList$Itr.next(ArrayList.java:831)
W/System.err:     at com.companyname.product.GameplayScene.update(GameplayScene.java:113)
//...

我相信这意味着它正在尝试对列表中不存在的内容执行操作。所以我的问题是,我能否得到一些帮助,以确定一个对象被调用的点不存在,因为我似乎找不到它。我知道它在哪个 class 中,因为每个错误的第一行都会引用它。

        for (Bullet bullet : player.getList())
            if (npcManager.bulletCollide(bullet)) { //checks for a collision between all npcs and all bullets
                 player.getList().remove(bullet);//npc is removed in method above

        }

       if (!gameOver) {
        npcManager.update(); //updates movement of npcs
        if (npcManager.playerCollideNPC(player)) {
            //end game
        }
    }

    for (RectNPC NPC : npcManager.getList())
        if (obstacleManager.NPCCollide(NPC)) { //checks collision between npcs and other objects
            //
        }

        int i = 0;
        //checks collisions between NPCs themselves
        while (i < (Constants.NUMBER_ENEMIES - 1)) {
            if (npcManager.getList().size() <= 1)
                return;
            if (Rect.intersects(npcManager.getList().get(i).getRectangle(), npcManager.getList().get(i + 1).getRectangle()))
                npcManager.getList().get(i).setRectangle(200);
            i += 1;
        }
    }

(上图)我想问题在于在对象不再可用的更新(如上所示)中调用某些内容,我删除 NPC 的方式是否有任何问题?

 public boolean bulletCollide(Bullet bullet) {
    for(RectNPC npc : monsters) {
        if(npc.bulletCollideNPC(bullet)) {
            monsters.remove(npc);
            monsters.add(0, new RectNPC(new Rect(100, 100, 200, 200), Color.rgb(255, 0, 0), 25));
            return true;
        }
    }
    return false;
}

(以上)我删除 NPC 的代码,我已经包括在内,以防它有帮助 - 但是它确实做了它的意思,所以我不认为这是问题所在。


我的主要问题是,这里是否有任何东西会导致此错误(游戏停止几帧并在顶部给出错误)- 或者是否有 something/somewhere 具体我应该在看?我也知道我的一些语法不是很好,很抱歉这是我的第一个 Java 项目。


当前迭代器:

for(Iterator<RectNPC> iterator = monsters.iterator(); iterator.hasNext();) {
        if(iterator.next().bulletCollideNPC(bullet)) {
            iterator.remove();
            monsters.add(0, new RectNPC(new Rect(100, 100, 200, 200), Color.rgb(255, 0, 0), 25));
            return true;
        }
    }

for (Iterator<Bullet> iterator = player.getList().iterator(); iterator.hasNext(); ) {
            if (npcManager.bulletCollide(iterator.next())) {
                iterator.remove();
                //
            }
        }

在 java 中,您不能执行 for( T variable : collection ) { collection.remove( variable ); } 换句话说,您不能从使用 for 循环迭代的集合中删除项目。它将导致 ConcurrentModificationException。 Google for "java fail-fast iterator" 了解有关原因和方式的更多信息。

您有两个选择:

  • 将所有要移除的怪物收集到一个临时列表中,然后在完成对主列表的迭代后,再次迭代临时列表以将它们从主列表中移除。关键是您不会从正在迭代的同一个列表中删除。

  • 而不是 "foreach" (for) 循环,使用实际的迭代器,当你想删除一个项目时,通过调用迭代器的 remove() 方法, 不是 包含集合的 remove() 方法。迭代器的 remove() 方法使用必要的技巧来防止 ConcurrentModificationException 被抛出。

您的方法正在做 3 件不同的事情。它返回一个布尔值,从列表中删除一个元素,然后添加一个元素。我会重组代码,以便您有 3 个方法只做 1 件事。

您可以使用 Java 8 内部迭代器的强大功能作为起点

      monsters.removeIf(e -> e.bulletCollideNPC(bullet));

使用迭代器以安全的方式移除元素。

Note that Iterator.remove is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress. From Java SE documentation

static void filter(Collection<?> c) {
    for (Iterator<?> it = c.iterator(); it.hasNext(); )
        if (!cond(it.next()))
            it.remove();
}

例子