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