ConcurrentModificationException al eliminar la viñeta del juego

I have to create Space Invaders for my programming course in college which would be fine but I have a problem with removing a bullet from the game.

I'm storing all my enemies and bullets in separate ArrayLists.

This is my main class.

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

And this is my Bullet class:

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

}

This is the exact error I am getting:

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)

I understand that I can use ListIterators to fix this but I can't figure out how to implement them in my main class.

preguntado el 27 de noviembre de 13 a las 00:11

2 Respuestas

Este bucle:

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

calls Bullet.update which can call dispose() in which you're doing

    SpaceInvaders.destroyBullet(this);

que invoca

    bullets.remove(b);

So essentially, there is a situation in which you're looping over an ArrayList and remove objects from it while looping. That is not allowed. You could use a CopyOnWriteArrayList instead, the operation is allowed there. This is rather expensive, though, as it copies the whole array when doing a modification. Another possible solution would be collecting the Bullets to be removed in dispose() and removing them after the update loop.

respondido 27 nov., 13:00

Thanks, I'm using the CopyOnWriteArrayList now. At the moment it doesn't matter how resource intensive it is as I'm only going to be running it on two machines maximum. - alfarero liam

This happens because you cannot iterate through the ArrayList and modify the elements in the same time. What you can do is

1)

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

2) Also (though I believe you should do the first thing) you can use a java.util.concurrent.CopyOnWriteArrayList and not change your code.

respondido 27 nov., 13:00

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.