Sunday, April 19, 2009

concurrentmodificationexception or: how I learned to stop worrying and loved for-each loop as it is

Few days ago, I got a ConcurrentModificationException out of nowhere while I was iterating through a list and removing elements from it. I remember doing this thing and not getting any exception. I delved a little bit and realized that the exception is thrown because of my misuse of for-each loop (introduced in jdk 5). Its developers noted:
Consider, for example, the expurgate method. The program needs access to the iterator in order to remove the current element. The for-each loop hides the iterator, so you cannot call remove. Therefore, the for-each loop is not usable for filtering. Similarly it is not usable for loops where you need to replace elements in a list or array as you traverse it. Finally, it is not usable for loops that must iterate over multiple collections in parallel.


For-each loop creates and hides the iterator, so when you try to remove an element using the list itself you'll get ConcurrentModificationException because you have to do the removing and adding through the iterator. So the code below will throw ConcurrentModificationException.

/*
integerList is an ArrayList
with Integer objects as elements
*/

for(Integer integer : integerList){
if(integer.intValue()%2 == 0) // remove even elements
integerList.remove(integer);
}


You can fix your error by not using for-each loop. For instance, you can define your iterator and work with it or you can iterate through the array list and do the add/remove directly from the list.

First one's code:


Iterator iterator = integerList.iterator(); // create the iterator
while(iterator.hasNext()){
Integer integer = iterator.next(); // take the element
if(integer.intValue()%2 == 0)
iterator.remove(); // remove using the iterator
}


Second approach's code:



for(int i=0 ; i < integerList.size(); i++){
Integer integer = integerList.get(i); // take the element
if(integer.intValue() %2 == 0){
// remove from the list
integerList.remove(i);
/*
decrease the index by one, so
that we won't skip any elements
after the element left-shifting which
occurs after each removal.
*/

i--;
}
}

No comments:

Post a Comment