2

I have a Program including a spaceship (big circle) which fires a bullet (small circle), that disappears after a while. Every time it runs over a specific part of the code it generates an error(, but it continues to run).

So I'm sure that the problem is in the following lines

private ArrayList<Bullet> bullets = new ArrayList<Bullet>();
//[...]
for (Bullet bullet : bullets) {
  if (bullet.getLoops() > 50) {

    bullets.remove(bullet); // specially in this line,
                            // because if I delete this, the error doesn't show up!
                            // I think it has something to do with the ArrayList.

  } else {
    bullet.next();
    gc.strokeOval(bullet.getX() - 3, bullet.getY() - 3, 6, 6);
  }
}

and this is the error I get:

Exception in thread "JavaFX Application Thread" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at sample.Main$4.handle(Main.java:131)
    at javafx.animation.AnimationTimer$AnimationTimerReceiver.lambda$handle$484(AnimationTimer.java:57)
    at java.security.AccessController.doPrivileged(Native Method)
    at javafx.animation.AnimationTimer$AnimationTimerReceiver.handle(AnimationTimer.java:56)
    at com.sun.scenario.animation.AbstractMasterTimer.timePulseImpl(AbstractMasterTimer.java:357)
    at com.sun.scenario.animation.AbstractMasterTimer$MainLoop.run(AbstractMasterTimer.java:267)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:506)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:490)
    at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$404(QuantumToolkit.java:319)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)

I will be delighted to supply any other information needed upon request.

Cheers. (Sorry for my English, I'm from Germany, but I have English in school since a few Years.)

PS: if anybody needs the full code of the mainclass:

package sample;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;

import java.util.ArrayList;

public class Main extends Application {

    private Player p = new Player(100, 100, 0);
    private ArrayList<Bullet> bullets = new ArrayList<Bullet>();

    private static boolean left;
    private static boolean right;
    private static boolean up;
    private static boolean down;
    private static boolean shoot;

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        Group root0 = new Group();
        Scene theScene = new Scene(root0, 500, 500);
        primaryStage.setScene(theScene);
        Canvas canvas = new Canvas(500, 500);
        root0.getChildren().add(canvas);


        theScene.setOnMouseMoved(
                new EventHandler<MouseEvent>() {
                    public void handle(MouseEvent e) {
                        Vector a = new Vector(p.getX(), -p.getY());
                        Vector b = new Vector(e.getX(), -e.getY());
                        Vector c = new Vector(p.getX(), -p.getY() + 1);
                        p.setRotation(Vector.getAngle(a, b, c));
                    }
                }
        );
        theScene.setOnKeyPressed(
                new EventHandler<KeyEvent>() {
                    public void handle(KeyEvent e) {
                        String code = e.getCode().toString();
                        if (code.equals("A")) {
                            left = true;
                        }
                        if (code.equals("D")) {
                            right = true;
                        }
                        if (code.equals("W")) {
                            up = true;
                        }
                        if (code.equals("S")) {
                            down = true;
                        }
                        if (code.equals("SPACE")) {
                            if (!PlayerShot()) shoot = true;
                        }
                    }
                }
        );
        theScene.setOnKeyReleased(
                new EventHandler<KeyEvent>() {
                    public void handle(KeyEvent e) {
                        String code = e.getCode().toString();
                        if (code.equals("A")) {
                            left = false;
                        }
                        if (code.equals("D")) {
                            right = false;
                        }
                        if (code.equals("W")) {
                            up = false;
                        }
                        if (code.equals("S")) {
                            down = false;
                        }
                        if (code.equals("SPACE")) {
                            shoot = false;
                        }
                    }
                }
        );
        GraphicsContext gc = canvas.getGraphicsContext2D();

        Font theFont = Font.font("Helvetica", FontWeight.BOLD, 24);
        gc.setFont(theFont);
        gc.setStroke(Color.BLACK);
        gc.setLineWidth(1);

        new AnimationTimer() {
            public void handle(long currentNanoTime) {
                // Clear the canvas
                gc.setFill(new Color(0.85, 0.85, 1.0, 1.0));
                gc.fillRect(0, 0, 500, 500);

                int x = p.getX();
                int y = p.getY();
                if (left && x >= 16) {
                    p.setX(x - 3);
                }
                if (right && x <= 500 - 16) {
                    p.setX(x + 3);
                }
                if (up && y >= 16) {
                    p.setY(y - 3);
                }
                if (down && y <= 500 - 16) {
                    p.setY(y + 3);
                }
                if (shoot) {
                    bullets.add(new Bullet(p.getX(), p.getY(), p.getRotationInVelocity(), p));
                    shoot = false;
                }
                gc.setStroke(Color.BLUE);
                gc.strokeLine(p.getX(), p.getY(), p.getX() + Math.sin(p.getRotation()) * 16, p.getY() - Math.cos(p.getRotation()) * 16);
                gc.strokeOval(p.getX() - 16, p.getY() - 16, 32, 32);
                gc.setStroke(Color.RED);
                for (Bullet bullet : bullets) {
                    if (bullet.getLoops() > 50) {
                        bullets.remove(bullet);
                    } else {
                        bullet.next();
                        gc.strokeOval(bullet.getX() - 3, bullet.getY() - 3, 6, 6);
                    }
                }
            }
        }.start();

        primaryStage.show();
    }

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

    // Hat der Spieler gerade eben schon geschossen?
    public boolean PlayerShot() {
        for (Bullet bullet : bullets) {
            if(bullet.getShooter() == p)return true;
        }
        return false;
    }
}

the other classes just handle a few int- and double-vars

doej1367
  • 311
  • 2
  • 12
  • public void next() { x += velocity.getY()*5; y += -velocity.getX()*5; loops++; } // this is the code which handles the moving forward and if I delete it it doesn't come to the part with the "bullets.remove(bullet);" so it can't show up than. But that's not the solution that I want! – doej1367 May 08 '16 at 20:02
  • thanks anyway :) ! – doej1367 May 08 '16 at 20:08

1 Answers1

2

You can't do that. You are getting a ConcurrentModificationException.

What is a ConcurrentModificationException?

From official Javadoc:

This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.

Basically, you are iterating over your list. If at one point you decide to remove an element, this may cause an unpredictable behaviour. To avoid this, this exception is thrown.

How do I fix it?

Simply use an Iterator instead to cycle your ArrayList. Iterator uses the methods add(T element) and remove(T element).

Aurasphere
  • 3,841
  • 12
  • 44
  • 71
  • 1
    You think like this? Iterator bulletsI = bullets.iterator(); while (bulletsI.hasNext()) { if (bulletsI.next().getLoops() > 50) { bullets.remove(bulletsI.next()); } else { bulletsI.next().next(); gc.strokeOval(bulletsI.next().getX() - 3, bulletsI.next().getY() - 3, 6, 6); } } it just throws even more Errors at me – doej1367 May 08 '16 at 20:24
  • 1
    But thanks! it helped me a little further knowing I'm on the right track – doej1367 May 08 '16 at 20:31
  • No. You are calling next() too may times. You should call it once per iteration. Next() will make the iterator go to the next element in the list. If you call 5 times per iteration after you check that he has at least one element I can see why you are getting more errors. Just do this after the while: Bullet currentElement = bulletsI.next(); and then do your stuff on this object without calling next() anywhere else. – Aurasphere May 08 '16 at 20:31
  • Oh right! I'll fix that! thanks! – doej1367 May 08 '16 at 20:33
  • 1
    Thanks! It works! Iterator bulletsI = bullets.iterator(); while (bulletsI.hasNext()) { Bullet bullet = bulletsI.next(); if (bullet.getLoops() > 50) { bulletsI.remove(); } else { bullet.next(); gc.strokeOval(bullet.getX() - 3, bullet.getY() - 3, 6, 6); } } – doej1367 May 08 '16 at 20:46