Java 在集合上迭代时从集合中移除对象

Java 在集合上迭代时从集合中移除对象,java,swing,synchronization,Java,Swing,Synchronization,我正在尝试删除JPanel之外的对象。 然而,当我这样做时,我得到了这个错误 我的程序崩溃了。 我的讲师告诉我,这是因为两个线程正在访问存储我的对象的ArrayList 我没有同步功能,但它没有工作 项目符号 public void move(){ if(y< -height){ synchronized (this) { bullet.remove(this); } }

我正在尝试删除JPanel之外的对象。 然而,当我这样做时,我得到了这个错误

我的程序崩溃了。 我的讲师告诉我,这是因为两个线程正在访问存储我的对象的ArrayList

我没有同步功能,但它没有工作

项目符号

public void move(){
        if(y< -height){
            synchronized (this) {
                bullet.remove(this);
            }
        }
        y-=5;
    }
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;

public class Bullet {
    //Environment
    public static ArrayList<Bullet> bullet = new ArrayList<>();
    private String path = Application.path;
    private GamePanel gp;
    //properties
    private int x,y;
    private int width,height;
    private int yVector;
    private Image image;


    Bullet(GamePanel gp, int x, int y){
        image = new ImageIcon(path+"\\images\\javaicon.png").getImage();
        width=image.getWidth(null);
        height=image.getHeight(null);
        this.gp=gp;
        this.x=x;
        this.y=y;
        yVector=5;
    }

    public void move(){
        if(y< -height){
           bullet.remove(this);
        }
        y-=5;
    }

    public Image getImg(){
        return image;
    }

    public int getX(){
        return x;
    }

    public int getY(){
        return y;
    }
}
游戏面板

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.security.Key;
import java.util.ArrayList;

public class GamePanel extends JPanel implements Runnable, KeyListener{
    private String path = Application.path;
    Image gameOverImg = new ImageIcon(path+"//images//gameover1.png").getImage();
    private Ihsan ihsan;
    private ArrayList <David> david = new ArrayList<>();
    private int enemies=5;
    private boolean pause=false;
    private boolean gameOver=false;

    GamePanel(){
        ihsan = new Ihsan(this);
        for(int i=0; i<enemies; i++){
            david.add(new David(this));
        }
        setFocusable(true);
        requestFocusInWindow();
        addKeyListener(this);
    }

    @Override
    public void run() {
        while (!pause){
            repaint();
            for(David david:david){
                david.move();
            }
            for(Bullet bullet:Bullet.bullet){
                bullet.move();
            }
            try{Thread.sleep(30);}
            catch (InterruptedException e){}
        }
    }

    public void paint(Graphics g){
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setColor(Color.GRAY);
        g2d.fillRect(0,0 ,getWidth(), getHeight());

        for(David david : david){
            g2d.drawImage(david.getImg(), david.getX(), david.getY(), null);
        }
        g2d.drawImage(ihsan.getImg(), ihsan.getX(), ihsan.getY(), null);
        for (Bullet bullet:Bullet.bullet){
            g2d.drawImage(bullet.getImg(), bullet.getX(), bullet.getY(), null);
        }

        if(gameOver){
            g2d.drawImage(gameOverImg,0,getHeight()/4,null);
        }
    }

    private static final Dimension DESIRED_SIZE = new Dimension(600,700);
    @Override
    public Dimension getPreferredSize(){
        return DESIRED_SIZE;
    }

    public void setGameOver(boolean gameOver) {
        this.gameOver = gameOver;
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int key=e.getKeyCode();

        if (key==KeyEvent.VK_D || key==KeyEvent.VK_RIGHT){
            ihsan.move(4,0);
            System.out.println("Right Key");
        }

        if (key==KeyEvent.VK_A || key== KeyEvent.VK_LEFT){
            ihsan.move(-4,0);
            System.out.println("Left Key");
        }

        if(key==KeyEvent.VK_SPACE){
            Bullet.bullet.add(new Bullet(this,ihsan.getX()+(ihsan.getWidth()/2), ihsan.getY()));
        }
    }

    @Override
    public void keyTyped(KeyEvent e) { }

    @Override
    public void keyReleased(KeyEvent e) { }

    public boolean getGameOver(){
        return gameOver;
    }
}
import javax.swing.*;
导入java.awt.*;
导入java.awt.event.KeyEvent;
导入java.awt.event.KeyListener;
导入java.security.Key;
导入java.util.ArrayList;
公共类GamePanel扩展了JPanel实现可运行的KeyListener{
私有字符串路径=Application.path;
Image gameOverImg=新图像图标(路径+“//images//gameover1.png”).getImage();
私人医院;
private ArrayList david=new ArrayList();
私人int=5;
私有布尔暂停=false;
私有布尔gameOver=false;
游戏面板(){
ihsan=新的ihsan(本);

对于(int i=0;i,
synchronized
块必须位于访问或修改
ArrayList
的每段代码的周围。括号中的对象必须相同:它是锁

例如,创建一个名为
bulletLock
Object
类型的字段,并在每次访问
bullet
时将其用作锁

发生此错误的原因是,当另一个线程位于列表上的for循环中时,您正在删除项目符号。由于存在并发修改,因此无法安全继续


另一个解决方案是在for循环之前复制
ArrayList

当前的问题不是同步,而是在迭代时修改项目符号列表:

// GamePanel.java#run():
    for (Bullet bullet:Bullet.bullet) { //your code is iterating over Bullet.bullet here
        bullet.move(); //you call Bullet#move here
    }

// Bullet.java#move():
    public void move(){
        if(y< -height){
           bullet.remove(this); //this will remove the current bullet from Bullet.bullet
           // ultimately causing the ConcurrrentModificationException in GamePanel.run()
        }
        y-=5;
    }

还有其他问题:

  • 您可以从自己的线程而不是Swing EDT调用
    #repaint()
  • 重新绘制在相同的
    Bullet.Bullet
    列表上迭代,而不进行同步(这可能导致
    GamePanel.paint()中出现
    ConcurrentModificationException

中所述问题的简单解决方案可以是:

for(Bullet bullet:  new ArrayList(Bullet.bullet) ){ //iterate over a copy
       bullet.move();
}
或者:

Iterator<Bullet> it = Bullet.bullet.iterator();
while (it.hasNext()) {
    Bullet bullet = it.next();
    bullet.move();
}
Iterator it=Bullet.Bullet.Iterator();
while(it.hasNext()){
Bullet=it.next();
bullet.move();
}

不更改代码的其他部分。

复制ArrayList不是效率低下吗?ArrayList中的项目符号将是相同的实例,因此可以工作,但会占用更多内存。最好是使用同步块。我尝试了同步,但仍然崩溃。我想我做错了什么。你忘记了同步nize the add-in keypress是的,我知道第二个问题。它在问题中。我正在寻找解决方案。我认为同步可以解决它。@ertagon2我已经用可能的解决方案更新了我的答案。非常感谢
for(Bullet bullet:  new ArrayList(Bullet.bullet) ){ //iterate over a copy
       bullet.move();
}
Iterator<Bullet> it = Bullet.bullet.iterator();
while (it.hasNext()) {
    Bullet bullet = it.next();
    bullet.move();
}