简体   繁体   中英

ArrayList Index out of bounds exception on only object in the arraylist

I know this has probably been asked many times before, but I have a problem and I could not find a solution that particularly targeted my issue.

The problem is as follows:

I have two arraylists of rockets and projectiles, and collision is checked between them. When they collide, both of them are removed. The problem is that when the only rocket in the arraylist is hit, the exception occurs, and I have no idea how to stop it from happening.

java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at adam.miszczak.defendthebase.level.Level.collision(Level.java:158)
at adam.miszczak.defendthebase.level.Level.tick(Level.java:144)
at adam.miszczak.defendthebase.Game.tick(Game.java:124)
at adam.miszczak.defendthebase.Game.run(Game.java:104)
at java.lang.Thread.run(Unknown Source)

Collision method + the arraylists:

public static ArrayList<Projectile> projectiles = new ArrayList<Projectile>();
private static ArrayList<Rocket> rockets = new ArrayList<Rocket>();

private void collision(){
    for(int i = 0; i < rockets.size(); i++){
        for(int j = 0; j < projectiles.size(); j++){
            try{
                if(rockets.get(i).bounds().intersects(projectiles.get(j).bounds())){
                    projectiles.remove(j);
                    rockets.get(i).die();
                    stats.addScore(rockets.get(i));
                    Particle p = new Particle(20, 20, 20, 50);
                    particles.add(p);
                }
            }catch(IndexOutOfBoundsException e){
                e.printStackTrace();
            }
        }
    }
}

Remove and Add methods:

private void remove(ArrayList<Projectile> projectiles, ArrayList<Rocket> rockets){
    for(int i = 0; i < rockets.size(); i++){
        if(rockets.get(i).getVisible()){ rockets.remove(i); rocketsOnScreen--; }
    }

    for(int i = 0; i < projectiles.size(); i++){
        if(projectiles.get(i).isRemoved()){ projectiles.remove(i);}
    }

    for(int i = 0; i < planes.size(); i++){
        if(planes.get(i).isRemoved()){ planes.remove(i); planesOnLevel--;}
    }
}

private void spawnRocket(int rocketType, int x, ArrayList<Rocket> rockets) {
    switch (rocketType) {
        case ROCKET_NORMAL:
            rockets.add(new NormalRocket(x, -10, 80, 0, 2));
            rocketsSpawned++;
            rocketsOnScreen++;
            break;
        case ROCKET_FIRE:
            if(difficulty > 1 && random.nextInt(100) > fireRocketSpawn){
                    rockets.add(new FireRocket(x, -10, 70, 0, 2));
                    rocketsSpawned++;
                    rocketsOnScreen++;
            }else{
                return;
            }
            break;
        case ROCKET_ZIPPER:
            if(difficulty > 2 && random.nextInt(100) > zipperRocketSpawn){
                rockets.add(new ZipperRocket(x, -10, 40, 0, 4));
                rocketsSpawned++;
                rocketsOnScreen++;
            }else{
                return;
            }
            break;
        case ROCKET_TANK:
            if(difficulty > 3 && random.nextInt(100) > tankRocketSpawn){
                rockets.add(new TankRocket(x, -10, 130, 0, 1));
                rocketsSpawned++;
                rocketsOnScreen++;
            }else{
                return;
            }
            break;
    }
} 

NOTE: rocket.die() basically just removes the rocket, the same way the remove method would do.

You cannot remove while iterating over a List with anything other than an Iterator . Imagine the consequences; you are looping over the elements and remove element n . All other elements shift down one to fill the gap. You take element n+1 , this is now element n+2 from when you started looping. And the List is one element smaller so your bounds check is now wrong. You can get all sorts of hard to track-down bugs in this manner.

In Java if you use the proper syntax - ie enhanced foreach loops then your will get the correct error if you even try to addto/deletefrom the list while iterating - you will get a ConcurrentModificationException .

To fix your method, 1) get rid of all the indices. You are abusing them and they are unnecessary (now many times do you get the same element from each List in each iteration?) . 2) use an actual Iterator for the inner loop so that you can call Iterator.remove to remove the last returned element from the List . Because that is done via the Iterator it doesn't result in an error:

private void collision() {
    for (final Rocket rocket : rockets) {
        final Iterator<Projectile> iter = projectiles.iterator();
        while (iter.hasNext()) {
            final Projectile projectile = iter.next();
            if (rocket.bounds().intersects(projectile.bounds())) {
                rocket.die();
                stats.addScore(rocket);
                Particle p = new Particle(20, 20, 20, 50);
                particles.add(p);
                iter.remove();
            }
        }
    }
}

Never remove items of a list during a loop through the list. It might give concurrency oroblems. Mark the numbers for removal right after the loop.

Not sure if it will fix your problem, but sure enough you should fix that too.

When the collision is detected you call

rockets.get(i).die();

'die' removes the rocket from the 'Arraylist', you say. Then you call

stats.addScore(rockets.get(i));

which will crash (this line is 158, isn't it? )

Apologies for brevity and formatting, first answer from my phone :-)

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