Using add/remove on Collections in enhanced for loops

Hello.
I’m using Collections for storage of many stuff. I have a lot of trouble making elegant code as when you loop in Collections with a enhanced for loop you are not supposed to change them, ConcurrentModification Exception is thrown. It is quite irritating, how do you guys deal with this problem?
For example, I have a list of items in player class. I have also a removeItem(item) and addItem(item) which adds or removes the item in the item list. These are useless if I call them when looping through items… like if I go through all items and detect broken ones, I cannot call removeItem(), I cannot even use the enhanced for loop. I must use ugly Iterator loop if I ever wanted to remove something from the list. So how to deal with this?

Im afraind an iterator loop is you only choice:


Iterator<T> iterator = someCollection.iterator();

while(iterator.hasNext()) {
  T object = iterator.next();

  ...
}

The new for loop doesn’t deal with removes, it was never part of the specs IIRC.

DP :slight_smile:

that totally sucks, what was designer thinking ::slight_smile:

Well since I’m updating entities in my game I won’t pass iterator around in every update so I can remove them (they die for example).
I think I will just create a singleton class for queueing addition and removal of stuff from the list, and then execute all of that at the end of game primary update. We’ll see if that works.

You can remove stuff on the go¹ if you use a usual reverse-for loop and an arraylist.

You can also remove stuff on the go¹ if you use a forward-for loop and a bag.

[¹ It has to be done as the very last thing in the loop.]


List removeLater = new ArrayList();
for(Object obj: group)
{
   // ....

   if(...)
      removeLater.add(obj);

   // ....
}

group.removeAll(removeLater);

:-\ :-X

Yes that would work Riven if I did not have to enter object.update() methodes inside the loop, where I need to call object.die() where it removes itself from the list… and I don’t have iterator there (and don’t plan to pass iterator everywhere)

I’ve written a small class that queues objects to be added / removed from any list. I know probably it could be written much more efficient (like I’m using 3 lists…), but I just want to game to work now, I’ll optimize it later.


/**
 * Provides static methodes for queueing add / remove stuff from
 * Collections as you can't do it inside enhanced for loop.
 * @author: Kova
 * 2008.01.12
 */
public class CollectionQueue {
    private static List<Boolean> actions = new ArrayList<Boolean>();
    private static List<Object> items = new ArrayList<Object>();
    private static List<Collection> collections = new ArrayList<Collection>();
    
    /**
     * Queue adding object to collection.
     * @param object
     * @param collection
     * @author Kova
     */
    public static void add(Object object, Collection collection) {
        actions.add(true);
        items.add(object);
        collections.add(collection);
    }
    
    /**
     * Queue removing object from collection.
     * @param object
     * @param collection
     * @author Kova
     */
    public static void remove(Object object, Collection collection) {
        actions.add(false);
        items.add(object);
        collections.add(collection);
    }
    
    /**
     * Execute stuff that is queued. You can call it whenever you
     * are not in enhanced for loop of collection that is in this queue.
     * @author Kova
     */
    public static void execute() {
        Iterator<Boolean> iterator = actions.iterator();
        for (int i=0; i<actions.size(); i++) {
            if (actions.get(i))
                collections.get(i).add(items.get(i));
            else
                collections.get(i).remove(items.get(i));
        }
        actions.clear();
        items.clear();
        collections.clear();
    }
    
}

Would it be possible to refactor the architecture so that the problem could be avoided? If it can’t be solved with refactoring, loop over a copy of the original collection. Then any modifications to the original collection will not affect your loop. Like this:


Collection<MyObject> stuff;
...
MyObject[] tmp = stuff.toArray(new MyObject[stuff.size()]);
for (MyObject thing : tmp) {
	thing.update(); // will call stuff.remove()
}

brilliant, I think I’ll go for this rather than queue removal or adding

EDIT:
Although in code I just duplicated the list itself, I do not think collections are that slow:

List<E> loopList = new ArrayList<E>(originalList);
for (E e : loopList) {....}