如何正确地为数据结构中的图像设置动画而不是获取ConcurrentModificationException

对于那些讨厌阅读长问题的人, 请使用下面的完整代码,运行它,点击SPACE几次,然后你会得到一个ConcurrentModificationException 简单的问题:你如何解决它? 问题是尝试在退出屏幕时从列表中删除Fireball Timer代码是问题所在。

如果您想了解更多信息,请继续阅读。

在OP询问如何射击火球图像的这个问题中,我回答了这个答案,表明应该使用数据结构来保存火球。 IMO是@ $$答案的一半。 我认为这是因为我提供的代码并不完整,因为它不考虑何时需要从数据结构中删除火球,比如当火球离开屏幕或者碰撞时与对方球员。 所以最终它只会变成一个无穷无尽的火球List ,我认为它不是有效的,也不是正确的做法。

这是我如何做到的。 有一个Fireball类可以保存火球和x和y位置的图像。 我所做的就是继续使用键绑定向List添加一个Fireball实例,并使用计时器移动Fireballx位置进行动画处理

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

在此处输入图像描述


所以我说这是一个不完整的答案,因为这个原因 – “因为它不考虑火球需要从数据结构中移除的时候,比如当火球从屏幕上移开或者是否与火箭相撞时对手“

我试图通过这样做考虑到这一点,如果它的x位置超过屏幕宽度,从列表中删除Fireball

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

但问题是,一旦Fireball到达屏幕的末尾并从List删除,我就会收到ConcurrentModificationException 。 我搜索了如何解决这个问题,有些人建议使用Iterator ,但是当我尝试这个时,当List存在许多Fireballs时,我仍然会遇到exception

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

所以我的问题是,动画这个场景的正确方法是什么(当它退出屏幕时从列表中删除球),以避免ConcurrentModificationException Timer代码是问题所在。

这是您可以运行的代码

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

您无法从for-each循环中删除List中的项目。 我不知道细节,但我知道它通常不起作用。

相反,获取ListIterator并使用它的remove方法代替……

 Iterator it = fireBalls.iterator(); while (it.hasNext()) { Fireball ball = it.next(); if (ball.x > D_W) { // You can't call this. The Iterator is backed by the ArrayList //fireBalls.remove(ball); it.remove(); } else { ball.x += X_INC; repaint(); } } 

快乐的火球垃圾邮件!