Search code examples
javaiteratorconcurrentmodification

Iterator remove throws ConcurrentException


Was trying to implement in-place purge on a list.

        Iterator<Integer> itr = ls.iterator();
        for (Integer x : ls) {
            int count = 0;
            while(itr.hasNext()){
                if (itr.next().equals(x)) {
                    count++;
                    if (count > 1) {
                        itr.remove();
                    }
                }
            }
        }

Exception in thread "main" java.util.ConcurrentModificationException

        Iterator<Integer> iterator = numbers.iterator();
        while (iterator.hasNext()) {
            if (iterator.next() == 1) {
                iterator.remove(); // Doesn't throw exception
            }
        }

However, with just one loop thought it doesn't. Why ?

what is the reasoning for this behavior


Solution

  • You have two iterators over ls here:

    • The one created explicitly via ls.iterator();
    • The one created implicitly via for (Integer x : ls).

    If you call remove() on the first iterator, it invalidates the second iterator, meaning any invocations of next() on the second iterator cause (or are prone to causing) a ConcurrentModificationException to be thrown.


    A possible way to do an in-place deduplication might be like this:

    int dst = 0;
    for (Integer x : ls) {
      if (!ls.subList(0, dst).contains(x)) {
        ls.set(dst++, x);
      }
    }
    ls.subList(dst, ls.size()).clear();
    

    This shifts elements that haven't been seen before to the left in the array; and then removes all other elements from the array afterwards.

    Changing the list in the loop doesn't result in a ConcurrentModificationException because there is no structural modification to the list.