2

My program throws ConcurrentModificationException when I run the following piece of code. Through some research I found that an element in the list cannot be added or removed when in an iterator loop. What do I do now to remove an element in the List<Bean>?

for (Iterator<Entry<String, List<Bean>>> iterator = dataMap.entrySet().iterator(); iterator.hasNext();) {
    Entry<String, List<Bean>> entry = (Entry<String, List<Bean>>)iterator.next();
    List<Bean> dateWiseValues = (List<Bean>) entry.getValue();
    int j = 0;
    for (Bean statBean : dateWiseValues) {
        for (int i = 0; i < commonElements.size(); i++) {
            if(statBean.getDate().equalsIgnoreCase(commonElements.get(i))) {
                //remove the bean
                entry.getValue().remove(j);
            }
        }
        j++;
    }
} 
seh
  • 14,999
  • 2
  • 48
  • 58
user1669488
  • 227
  • 1
  • 2
  • 10
  • 1
    remove it later or you can copy your values to a 'mirror' list and iterate the mirror while removing them from the actual list – 0x6C38 Apr 19 '13 at 23:55
  • this might also be of interest: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/CopyOnWriteArrayList.html – Đinh Carabus Apr 20 '13 at 00:00
  • @Subarachnid, thats much better, post it as an answer – 0x6C38 Apr 20 '13 at 00:01
  • @MrD : I can't remove it later, cuz I only have chance to remove when in that loop as I'm comparing the values in that loop... But I believe cloning the list would be the only solution? – user1669488 Apr 20 '13 at 00:03

2 Answers2

5

Rather than using the range-based for loop over dateWiseValues, use an Iterator explicitly so that you can instead invoke Iterator#remove():

for (final Iterator<? extends Bean> it = dateWiseValues.iterator();
     it.hasNext();) {
  final Bean statBean = it.next();
  for (int i = 0, last = commonElements.size(); i != last; ++i)
    if (statBean.getDate().equalsIgnoreCase(commonElements.get(i)))                
      it.remove();
}

In general, it's safe to remove elements from and underlying collection while iterating over it so long as you do so through one of the collection's iterators.


Though we can't see the concrete type of dateWiseValues, we know it's a subtype of List. Two common concrete types implementing List are ArrayList and LinkedList. The prose class-level documentation for each of those classes contains the following admonishment:

The iterators returned by this class's iterator and listIterator methods are fail-fast: 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. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Note that it's warning that you must use Iterator#remove() for mutation during iteration or you'll face a thrown ConcurrentModificationException the next time you use that same or any other iterator walking over the same underlying list.

seh
  • 14,999
  • 2
  • 48
  • 58
2

When you use an enhanced for loop, it implicitly uses an Iterator behind the scenes. This is the Iterator throwing the ConcurrentModificationException, not the explicit iterator iterator you've defined in the outer for loop. You cannot remove from a list while an Iterator is iterating on it, unless you call Iterator#remove(), which removes the current list item and avoids the ConcurrentModificationException.

Your two options:

  • Rewrite the enhanced for loop to explicitly use a while loop and an Iterator, then call remove() when you want to remove an item.
  • Create a List of items to remove. When you determine that you want to remove that item, add it to the items to remove list. Then, after you've finished iterating, call removeAll on the original list to remove all the items you wish to remove.
rgettman
  • 176,041
  • 30
  • 275
  • 357