ConcurrentModificationException using Iterator

2020-05-01 23:53发布

I'm using an iterator to loop over a collection as follows:

Iterator<Entity> entityItr = entityList.iterator(); 

    while (entityItr.hasNext())
    {
        Entity curr = entityItr.next();

        for (Component c : curr.getComponents())
        {
            if (c instanceof PlayerControlled)
            {
                ((PlayerControlled) c).pollKeyboard();  
            }
        }
    }

However on the following line I get a ConcurrentModificationException

 Entity curr = entityItr.next();

Why is this happening when I'm not altering anything?

Many thanks

Edit - stack trace:

java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at cw.systems.Input.checkInputs(Input.java:31)
at cw.systems.Input.begin(Input.java:21)
at cw.misc.Game.render(Game.java:73)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:207)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:114)

标签: java iterator
2条回答
▲ chillily
2楼-- · 2020-05-02 00:23

You must be modifying the list either:

  1. inside your iterator in the pollKeyboard method, without using the add or remove methods on the iterator; or
  2. in another thread

Therefore your exception is the expected behaviour. From the docs, if you have a single thread iterating the list:

if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException

and if multiple threads uses the list at one time:

Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally

Solution:

If only one thread accesses the list, make sure you use the entityItr.remove or add methods to modify the list.

For the multi-threaded case you can use Collections.synchronizedList if you do not have a locking object available.

First store a single central reference to your list as:

entityList = Collections.synchronizedList(theOriginalArrayList);

And then access it (with all readers and writers) as:

synchronized (entityList) {
  // Readers might do:
  itr = entityList.iterator();
  while (i.hasNext())
    ... do stuff ...
}

There are other ways to sync multi-threaded access, including copying the list to an array (inside a sync block) and iterating it for reading, or using a ReadWrite lock. They all depend on your exact requirement.

查看更多
Anthone
3楼-- · 2020-05-02 00:39

It looks that there is another thread using the same collection and modifing it when this code is iterating over the collection.

ConcurrentModificationException

You can use navite java concurrent collestions instead. They are thread safe. However it's a good habbit to create immutable collections - they are thread safe and enforce you to design reliable code.

查看更多
登录 后发表回答