从游戏中删除子弹时出现ConcurrentModificationException

时间:2013-11-27 00:21:53

标签: java arraylist concurrentmodification

我必须为我在大学的编程课程创建太空入侵者,这很好,但我在游戏中移除子弹时遇到了问题。

我将所有敌人和子弹存储在单独的ArrayLists中。

这是我的主要课程。

package uk.ac.wnc.pot13000765.space_invaders;

import java.util.ArrayList;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

public class SpaceInvaders {

    private ArrayList<Enemy> enemies = new ArrayList<Enemy>();
    private static ArrayList<Bullet> bullets = new ArrayList<Bullet>();
    private Player player;

    public SpaceInvaders() {

        //Setup and create the OpenGL Context + Window

        try {

            Display.setDisplayMode(new DisplayMode(800, 600));
            Display.setTitle("Unit 14 Space Invaders in Java by Liam Potter - POT13000765");
            Display.create();

            GL11.glMatrixMode(GL11.GL_PROJECTION);
            GL11.glLoadIdentity();
            GL11.glOrtho(0, 800, 0, 600, 1, -1);
            GL11.glMatrixMode(GL11.GL_MODELVIEW);

            GL11.glClearColor(0f, 0f, 0f, 1f);
            GL11.glEnable(GL11.GL_BLEND);
            GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

        } catch(LWJGLException e) {
            System.err.print(e);
            System.exit(1);
        }

        Time.getDelta();
        Time.setLastFps(Time.getTime());

        //Setup
        player = new Player(40, 40, 50, 50);

        //Enemies
        enemies.add(new Enemy(40, 540, 25, 25));

        while(!Display.isCloseRequested()) {
            gameLoop(Time.getDelta());
        }

        Display.destroy();
    }

    private void gameLoop(int delta) {
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

        //Update
        player.update(delta);
        if(enemies.size() > 0) for(Enemy e: enemies) e.update(delta);
        if(bullets.size() > 0) for(Bullet b: bullets) b.update(delta);

        //Render
        player.draw();
        if(enemies.size() > 0) for(Enemy e: enemies) e.draw();
        if(bullets.size() > 0) for(Bullet b: bullets) b.draw();

        Display.sync(60);
        Display.update();
        Time.updateFps();
    }

    public int getWindowWidth() {
        return Display.getWidth();
    }

    public int getWindowHeight() {
        return Display.getHeight();
    }

    public static void spawnBullet(Bullet b) {
        bullets.add(b);
    }

    public static void destroyBullet(Bullet b) {
        bullets.remove(b);
    }

    public static void main(String[] args) {
        new SpaceInvaders();
    }
}

这是我的子弹课程:

package uk.ac.wnc.pot13000765.space_invaders;

import org.lwjgl.opengl.GL11;

public class Bullet extends Entity {

    public Bullet(int x, int y, int width, int height) {
        super(x, y, width, height);
    }

    @Override
    public void update(int delta) {
        if(!(getY() > 600)) setY(getY() + 1);
        else dispose();

        System.out.println(getY());
    }

    @Override
    public void draw() {
        GL11.glColor3f(1.0f, 0.5f, 0.5f);

        GL11.glBegin(GL11.GL_QUADS);
            GL11.glVertex2i(getX(), getY());
            GL11.glVertex2i(getX() + getWidth(), getY());
            GL11.glVertex2i(getX() + getWidth(), getY() + getHeight());
            GL11.glVertex2i(getX(), getY() + getHeight());
        GL11.glEnd();
    }

    @Override
    public void dispose() {
        SpaceInvaders.destroyBullet(this);
    }

}

这是我得到的确切错误:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at uk.ac.wnc.pot13000765.space_invaders.SpaceInvaders.gameLoop(SpaceInvaders.java:62)
at uk.ac.wnc.pot13000765.space_invaders.SpaceInvaders.<init>(SpaceInvaders.java:50)
at uk.ac.wnc.pot13000765.space_invaders.SpaceInvaders.main(SpaceInvaders.java:91)

我知道我可以使用ListIterators来解决这个问题,但我无法弄清楚如何在我的主类中实现它们。

2 个答案:

答案 0 :(得分:3)

这个循环:

    if(bullets.size() > 0) for(Bullet b: bullets) b.update(delta);

调用Bullet.update,它可以调用你正在做的dispose()

    SpaceInvaders.destroyBullet(this);

调用

    bullets.remove(b);

基本上,有一种情况是你循环遍历ArrayList并在循环时从中删除对象。这是不允许的。您可以使用CopyOnWriteArrayList,在那里允许操作。然而,这是相当昂贵的,因为它在进行修改时复制整个阵列。另一种可能的解决方案是收集要在dispose()中删除的项目符号,并在更新循环后删除它们。

答案 1 :(得分:0)

这是因为您无法迭代ArrayList并同时修改元素。你能做的是

1)

Iterator< Bullet > it = bullets.iterator();
while (it.hasNext() )
{
  Bullet b = it.next();
  bullets.update(b)  
}

2)另外(虽然我相信你应该做第一件事)你可以使用java.util.concurrent.CopyOnWriteArrayList而不是改变你的代码。