简体   繁体   中英

Iterating over Hashmap causes crash

I've noticed my app has been crashing a lot lately and after debugging I found out that when I iterated over hashmaps some crashed randomly.

I iterated over them like that:

for (Map.Entry<Point, Particle> entry : spawned.entrySet()) {
                entry.getValue().Draw(canvas);
        } 

which caused: 在此处输入图片说明

after that I tried to iterate over them with iterator like that:

        Iterator<Map.Entry<Point, Particle>> entries = spawned.entrySet().iterator();
    while (entries.hasNext()) {
        Map.Entry<Point, Particle> entry = entries.next();
        entry.getValue().Draw(canvas);
    }

but that caused: 在此处输入图片说明

here is inside Particle class if anyone suspects it has to do something with it:

public class Particle {
    Bitmap bit;
    int alpha = 0;
    boolean appeared = false;
    boolean dissapiread = false;
    int time = 1;
    PointF position;
    Handler handler;
    int counter = 0;
    PointF newpose;
    float newsize=0;
    int timer2=0;
    public Particle(Bitmap b, PointF p, int time) {
        int rand=(randInt(-b.getWidth()/2, b.getWidth()));
        this.bit = Bitmap.createScaledBitmap(b, b.getWidth()+rand,b.getHeight()+rand,false);
        this.position = p;
        this.time = time;
        newpose= new PointF();
        newpose.x=p.x+randInt(-15000, 15000);
        newpose.y=p.y+randInt(-15000,15000);

    }

    public void Draw(Canvas c) {
        update();
        Paint paintAlpha = new Paint();
        paintAlpha.setAlpha(alpha);
        c.drawBitmap(bit, position.x, position.y, paintAlpha);
    }
    public static int randInt(int min, int max) {
        Random rand = new Random();
        int randomNum = rand.nextInt((max - min) + 1) + min;
        return randomNum;
    }
    private void update() {
        if(position.x<newpose.x){
            position.x+=0.10f;
        }
         if(position.x>newpose.x){
            position.x-=0.10f;
        }
        if(position.y<newpose.y){
            position.y+=0.10f;
        }
         if(position.y>newpose.y){
            position.y-=0.10f;
        }
         if(dissapiread==false){
         if(timer2==0){
             newsize+=0.10;
             try {
                 bit=Bitmap.createScaledBitmap(bit, bit.getWidth()-(int)newsize,bit.getHeight()-(int)newsize,false);
            } catch (Exception e) {
                // TODO: handle exception
            }
         timer2=5;
         }
         else
             timer2--;
         }

        if (appeared == false) {
            if (alpha <=240) {
                alpha =alpha+10;
            } 
            else {
                if(counter==0){

                counter=time;
                }
                if(counter==1){
                    appeared=true;
                }
                else
                    counter--;
            }
        } else {
            if (dissapiread == false) {
                if (alpha != 0) {
                    alpha=alpha-10;
                }
                else
                    dissapiread = true;
            }
        }
    }
}

Please notice that I know that in my current code I don't have to use hashmaps, I just want to understand why it crashes because it happened in other places too.

edit: whole code:

public class gameview extends SurfaceView {

    private GameLoopThread gameLoopThread;
    private SurfaceHolder holder;
    Bitmap partic;
    Particle pp;
    Map<Point, Particle> spawned = new HashMap<Point, Particle>();
public gameview(Context c) {
    super(c);
    partic=BitmapFactory.decodeResource(getResources(),
            R.drawable.particle_fire);
     pp=new Particle(partic, new PointF(0,0), 100);
    gameLoopThread = new GameLoopThread(this);
    this.requestFocus();
    this.setFocusableInTouchMode(true);
    holder = getHolder();
    holder.addCallback(new SurfaceHolder.Callback() {

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            boolean retry = true;
            gameLoopThread.setRunning(false);
            while (retry) {
                try {
                    gameLoopThread.join();
                    retry = false;
                } catch (InterruptedException e) {
                }
            }
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            gameLoopThread.setRunning(true);
            gameLoopThread.start();
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format,
                int width, int height) {
        }
    });
}
@Override
public void onDraw(Canvas canvas) {
    // TODO Auto-generated method stub
    canvas.drawColor(Color.BLACK);
    pp.Draw(canvas);
        Iterator<Map.Entry<Point, Particle>> entries = spawned.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<Point, Particle> entry = entries.next();
            entry.getValue().Draw(canvas);
        }
}
@Override
public boolean onTouchEvent(MotionEvent event) {
    float x = (float) (event.getX());
    float y = (float) (event.getY());
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
        spawned.put(new Point((int)x,(int)y), new Particle(partic, new PointF(x,y), 100));
        break;
    }
    return super.onTouchEvent(event);
}
private void newParticle(int x,int y){

    spawned.put(new Point(0,0), new Particle(partic, new PointF(x,y), 100));
}

}

You update Partice in its Draw method. That leads to the ConcurrentModificationException you experiece in the loop.

If you want to avoid this kind of Exception, you can create a clone of the Collection of Particles over which you iterate and modify the original data. However, cloning might be pretty cumbersome for what you need to do.

With cloning I mean to copy the keys of the HashMap into a new Set and iterate over the set. Within the loop you can access the the HashMap value by the key you iterate over. Modifying the Particle should be fine. Note that if the update changes the HashValue of the object, you need to be careful.

Edit:

As @fge pointed out, the use of a CuncurrentHashMap also can solve the problem just fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM