Cómo animar correctamente imágenes en una estructura de datos y no obtener ConcurrentModificationException

Para aquellos que odian leer preguntas largas,Toma el código completo a continuación, ejecútalo, pulsaSPACE un par de veces, y obtendrá unConcurrentModificationException. Pregunta simple: ¿Cómo lo arreglas? El problema es tratar de eliminar unFireball de la lista cuando sale de la pantalla. losTimer El código es donde reside el problema.

Si quieres más información, sigue leyendo.

Enesta pregunta donde el OP pregunta cómo disparar imágenes de bola de fuego, respondí conesta respuesta lo que indica que una estructura de datos se debe utilizar para mantener las bolas de fuego. En mi opinión, fue una media @ $ respuesta. La razón por la que creo que esto se debe a que el código que proporcioné no está completo, ya que no tiene en cuenta cuándo es necesario eliminar una bola de fuego de la estructura de datos; con un jugador contrario. Así que en última instancia, simplemente se convierte en un sin finList de bolas de fuego, que no creo que sea eficiente ni la forma correcta de hacerlo.

Así es como lo hice. Hay unaFireball clase que contiene una imagen para la bola de fuego y las ubicaciones x e y. Todo lo que hice fue seguir agregando unFireball instancia a laList con un enlace de teclas y animado con temporizador moviendo elx ubicación de laFireball

Timer timer = new Timer(40, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        for (Fireball ball : fireBalls) {
            ball.x += X_INC;
            repaint();
        }
    }
});
...
getActionMap().put("hadouken", new AbstractAction() {
    @Override
    public void actionPerformed(ActionEvent e) {
        fireBalls.add(new Fireball(fireball));
    }
});

Así que dije que esta es una respuesta incompleta por esta razón."porque no tiene en cuenta cuándo es necesario quitar una bola de fuego de la estructura de datos, por ejemplo, cuando la bola de fuego se mueve fuera de la pantalla o si debía chocar contra un jugador contrario"

I hizo tratar de tener esto en cuenta al hacer esto, eliminando elFireball de la lista si esx Posición superó el ancho de la pantalla

Timer timer = new Timer(40, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        for (Fireball ball : fireBalls) {
            if (ball.x > D_W) {
                fireBalls.remove(ball);
            } else {
                ball.x += X_INC;
                repaint();
            }
        }
    }
});

El problema con esto, sin embargo, es que una vezFireball llega al final de la pantalla y se eliminará de laList, Yo tengo unConcurrentModificationException. Busqué cómo arreglar esto, y algunos sugirieron usar unIterator, pero cuando intenté esto, todavía tengo la excepción cuando muchosFireballs existe en elList

public void actionPerformed(ActionEvent e) {
    Iterator<Fireball> it = fireBalls.iterator();
    while (it.hasNext()) {
        Fireball ball = it.next();
        if (ball.x > D_W) {
            fireBalls.remove(ball);
        } else {
            ball.x += X_INC;
            repaint();
        }
    }
}

Así que mi pregunta es, ¿cuál es la forma correcta de animar este escenario (eliminando la bola de la Lista cuando sale de la pantalla), para evitar laConcurrentModificationException? losTimer El código es donde reside el problema.

Aquí está el código que puede ejecutar

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.List;
import java.util.logging.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.Timer;

public class WannaBeStreetFighter extends JPanel {

    private static final int D_W = 700;
    private static final int D_H = 250;
    private static final int X_INC = 10;

    List<Fireball> fireBalls;
    BufferedImage ryu;
    BufferedImage fireball;
    BufferedImage background;

    public WannaBeStreetFighter() {

        try {
            ryu = ImageIO.read(new URL("http://www.sirlin.net/storage/street_fighter/ryu_hadoken_pose.png?__SQUARESPACE_CACHEVERSION=1226531909576"));
            background = ImageIO.read(new URL("http://fightingstreet.com/folders/variousinfofolder/ehondasbath/hondasfz3stage.gif"));
            fireball = ImageIO.read(new URL("http://farm6.staticflickr.com/5480/12297371495_ec19ded155_o.png"));
        } catch (IOException ex) {
            Logger.getLogger(WannaBeStreetFighter.class.getName()).log(Level.SEVERE, null, ex);
        }

        fireBalls = new LinkedList<>();

        Timer timer = new Timer(40, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                for (Fireball ball : fireBalls) {
                    if (ball.x > D_W) {
                        fireBalls.remove(ball);
                    } else {
                        ball.x += X_INC;
                        repaint();
                    }
                }
            }
        });
        timer.start();

        InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        inputMap.put(KeyStroke.getKeyStroke("SPACE"), "hadouken");
        getActionMap().put("hadouken", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                fireBalls.add(new Fireball(fireball));
            }
        });

    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(background, 0, 0, D_W, D_H, this);
        g.drawImage(ryu, 50, 125, 150, 115, this);
        for (Fireball ball : fireBalls) {
            ball.drawFireball(g);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(D_W, D_H);
    }

    private class Fireball {

        Image fireball;
        int x = 150;
        int y = 125;

        public Fireball(Image image) {
            fireball = image;
        }

        public void drawFireball(Graphics g) {
            g.drawImage(fireball, x, y, 50, 50, null);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("Best Street Fighter ever");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new WannaBeStreetFighter());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

Respuestas a la pregunta(1)

Su respuesta a la pregunta