简体   繁体   中英

Concurrent modification exception when using iterator? (Java)

I am trying to write code for the simulation WaTor in which sharks and fish eat each other and simulate population control etc. Anyways, the problem I am having is that i keep getting a concurrect modification exception even when using an iterator.

Here is my code:

private void updateSharks() {   
    for(Iterator<Shark> sharkit = sharks.iterator(); sharkit.hasNext();) {
        Shark sharky = sharkit.next();
        if(sharky.hasStarved()) {
            sharkit.remove();
        }
        else if(sharky.canBreed()) {
            addNewShark(sharky.getX(),sharky.getY());
        }
        moveShark(sharky);
    }
}

and here is the exception:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at WaTor.Ocean.updateSharks(Ocean.java:281)
at WaTor.Ocean.update(Ocean.java:307)
at WaTor.Main.main(Main.java:13)

Line 281 in ocean is "Shark sharky = sharkit.next();" Thank you for any help!

You cannot modify a List while traversing it. If you are removing elements from a list while traversing then - One option to fix it would be to put these elements to be removed in another list and then looping over that list to remove elements. - Other option would be to use Iterator.remove() You are using Iterator.remove(). So that is fine.

If you are adding elements while traversing that can also be a problem. I haven't tested your code. But make sure you are not adding elements to list sharks in methods addNewSharks() and moveSharks()

Please have a look at the JavaDoc for the Iterator . For the remove method you can find the following text:

Removes from the underlying collection the last element returned by this iterator (optional operation). This method can be called only once per call to next(). The behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling ths method.

To me it looks like you are adding elements to the collection while iterating over it. Hence you may experience an unspecified behaviour.

As others mentioned, you can't modify collection while iterating (remove is optional). Well, you can gather all the sharks that should die in another collection, and remove them after the iterator loop terminates.

Try the following:

private void updateSharks() { 
    ArrayList<Shark> toRemove = new ArrayList<Shark>();
    ArrayList<Shark> toAdd = new ArrayList<Shark>();

    for(Iterator<Shark> sharkit = sharks.iterator(); sharkit.hasNext();) {
        Shark sharky = sharkit.next();
        if(sharky.hasStarved()) {
            toRemove.add(sharky);
        }
        else if(sharky.canBreed()) {
            toAdd.add(/*create new shark object here*/)
        }
        moveShark(sharky);
    }

    for (Shark shark : toRemove) {
        sharks.remove(shark);
    }
    for (Shark shark: toAdd) {
        sharks.add(shark);
    }
}

EDIT: How it works

You are iterating through the collection. At this step, you can do whatever you wish within the objects contained in the collection, but you cannot alter the collection itself, because iterator does not allow you to.

So you create a second, temporary collection. Every time you encounter an object which you want to remove from the main collection, you add this object to the temporary collection. Because in Java only references are passed and there is no deep copying here, and it is the same object contained in two collections at the same time.

Now, when the iteration is over, you have all the objects (or their references, if you prefer to think about it that way) contained within your temporary collection. Because the iterator is gone, nothing is restraining you from removing all these objects from the primary collection, and it is easily done!

Don't use iterator. Iterate through the collection backwards like this:

import java.util.ArrayList;

public class RemoveFromCollectionExample {

    //
    // instance variables
    //

    private ArrayList<Shark> sharks;

    //
    // main
    //

    public static void main(String[] args) {
        RemoveFromCollectionExample example = new RemoveFromCollectionExample();
        example.sharks = new ArrayList<RemoveFromCollectionExample.Shark>();
        example.addShark(false, false, 0, 0);
        example.addShark(false, false, 0, 0);
        example.addShark(false, false, 0, 0);
        example.addShark(false, false, 0, 0);
        example.addShark(false, false, 0, 0);
        example.addShark(false, false, 0, 0);
        example.addShark(false, true, 0, 0);
        example.addShark(true, false, 0, 0);
        example.addShark(true, false, 0, 0);
        example.addShark(true, false, 0, 0);
        System.out.println("START WITH: " + example.sharks.size());
        example.updateSharks();
        System.out.println("Added 1, Removed 3");
        System.out.println("ENDED WITH: " + example.sharks.size());
    }

    //
    // update sharks
    //

    private void updateSharks() {
        for (int i = this.sharks.size() - 1; i > 0; i--) {
            Shark sharky = this.sharks.get(i);
            if (sharky.hasStarved()) {
                this.sharks.remove(i);
                System.out.println("REMOVED");
            } else if (sharky.canBreed()) {
                addNewShark(sharky.getX(), sharky.getY());
                System.out.println("ADDED");
            }
            moveShark(sharky);
        }
    }

    //
    // add new shark method
    //

    private void addNewShark(int x, int y) {
        this.sharks.add(new Shark(false, false, x, y));
    }

    private void addShark(boolean hasStarved, boolean canBreed, int x, int y) {
        this.sharks.add(new Shark(hasStarved, canBreed, x, y));
    }

    //
    // move method
    //

    private void moveShark(Shark sharky) {
    }

    //
    // Shark class
    //

    private class Shark {

        private boolean hasStarved;
        private boolean canBreed;
        private int x;
        private int y;

        public boolean hasStarved() {
            return hasStarved;
        }

        public void setHasStarved(boolean hasStarved) {
            this.hasStarved = hasStarved;
        }

        public boolean canBreed() {
            return canBreed;
        }

        public void setCanBreed(boolean canBreed) {
            this.canBreed = canBreed;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }

        public Shark(boolean hasStarved, boolean canBreed, int x, int y) {
            super();
            this.hasStarved = hasStarved;
            this.canBreed = canBreed;
            this.x = x;
            this.y = y;
        }

        public void moveShark() {
        }

    }

}

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