Java如何在循环时添加到数组列表

时间:2014-05-05 09:53:19

标签: java arraylist concurrentmodification

package biz.boulter.state;

import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;

import biz.boulter.sword.Game;
import biz.boulter.sword.Particle;

public class Menu implements State{
private ArrayList<Particle> particles = new ArrayList<Particle>();
boolean inLoop = false;

public Menu(){

}

@Override
public void render(Graphics2D g) {
    if(!inLoop){
        for(Particle p: particles){
            p.render(g);
        }
    }
}

@Override
public void tick() {
    if(!inLoop){
        for(Particle p: particles){
            p.tick();
        }
    }
}

@Override
public void keyPressed(int kc) {

}

@Override
public void keyReleased(int kc) {

}

@Override
public void mousePressed(int button, int x, int y) {
    for(int i = 0; i<500; i++){
        int rand;
        if(Game.rand.nextBoolean()){
            rand = Game.rand.nextInt(10)-11;
        }else{
            rand = Game.rand.nextInt(10)+1;
        }
        particles.add(new Particle(x, y, rand, Game.rand.nextInt(10)-11, new Color(Game.rand.nextInt(1000000000))));
    }
}

@Override
public void mouseReleased(int button, int x, int y) {

}
}

嘿伙计这是我的代码,我不断收到ConcurrentModificationException。我知道这意味着两件事同时改变粒子变量。但是我应该如何添加到数组列表中。我看到一些论坛说使用迭代器,但这是为了删除不添加。

提前致谢!

编辑:

堆栈跟踪:

Exception in thread "Thread-2" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at biz.boulter.state.Menu.render(Menu.java:21)
    at biz.boulter.sword.Game.render(Game.java:43)
    at biz.boulter.sword.Game.run(Game.java:136)
at java.lang.Thread.run(Unknown Source)

粒子类:

package biz.boulter.sword;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;

public class Particle {
private double x = 0;
private double y = 0;
private double xa = 0;
private double ya = 0;
private Color particleColour;
private Rectangle img;

public Particle(int x, int y, int xa, int ya, Color colour){
    this.x = x;
    this.y = y;
    this.xa = xa;
    this.ya = ya;
    this.particleColour = colour;
    img = new Rectangle(x, y, 5, 5);
}

public void render(Graphics2D g){
    img.setBounds((int)Math.round(x), (int)Math.round(y), 5, 5);
    g.setColor(particleColour);
    g.fill(img);
}

public void tick(){
    ya+=0.5;

    if(xa < 0){
        xa+=1;
    }

    if(xa > 0){
        xa-=1;
    }

    x+=xa;
    y+=ya;
}
}

4 个答案:

答案 0 :(得分:3)

您有2个选项。如果可以,请将方法tickrendermousePressed标记为synchronized。这将解决问题,但如果插入速度更快,这可能不是最好的解决方案。

另一种解决方案是改变

private ArrayList<Particle> particles = new ArrayList<Particle>(); 

private LinkedBlockingQueue<Particle> particles = new LinkedBlockingQueue<Particle>();

这是有效的,因为iterator方法的documentation

  

返回的迭代器是一个&#34;弱一致的&#34;将要的迭代器   永远不会抛出ConcurrentModificationException,并保证   在构造迭代器时存在的遍历元素,   并且可能(但不保证)反映任何修改   在施工之后。

答案 1 :(得分:2)

最简单的方法是在使用时锁定集合。

synchronized(particles) {
    for(Particle p: particles){
        p.tick();
    }
}

Particle p = new Particle(x, y, rand, Game.rand.nextInt(10)-11, new Color(Game.rand.nextInt(1000000000)))
synchronized(particles) {
    particles.add(p);
}

这样可以确保访问不是并发的。

CopyOnWriteArrayList的问题是写入很昂贵。顾名思义,每次更新都需要整个列表的副本。

BlockingQueue的问题在于它不是为迭代而设计的。它会起作用,但效率不高。

答案 2 :(得分:1)

为了避免在集合的迭代中出现问题和ConcurentModificationException,你必须使用并发集合类。

对于ArrayList,您可以尝试使用CopyOnWriteArrayList ArrayList的线程安全变体。

有关此内容的更多详细信息,请参阅此处:http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CopyOnWriteArrayList.html

答案 3 :(得分:0)

你正试图改变数组的内部状态,在循环时,这是预期的行为。

一个快速(但有一些性能成本)是使用然后CopyOnWriteArrayList,请参阅链接条目,  In what situations is the CopyOnWriteArrayList suitable?