Search code examples
javaiteratorconcurrentmodification

Two iterators throw ConcurrentModificationException


I have the following code

public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        Arrays.stream("hello how are you".split(" ")).forEach(s -> list.add(s));

        Iterator<String> it = list.iterator();
        ListIterator<String> lit = list.listIterator();

        while (it.hasNext()) {
            String s = it.next();
            if (s.startsWith("a")) {
                it.remove();
            } else {
                System.out.println(s);
            }
        }

        System.out.println(list);

        // {here}

        while (lit.hasNext()) {
            String s = lit.next();
            if (s.startsWith("a")) {
                lit.set("1111" + s);
            } else {
                System.out.println(s);
            }
        }

        System.out.println(list);
}

Here, after iterating through the Iterator, I try to iterate through the ListIterator. But the code throws a ConcurrentModificationException. I do the modification using the ListIterator only after the Iterator is done, but why do I get this exception.

When I initalize the ListIterator at {here} instead at the top, the code runs perfectly.

  1. Isn't ConcurrentModificationException thrown when the list is being modified by two threads simultaneously?
  2. Does initializing the iterator, create a lock on the list ? If yes, then why does Java let us to initialize an Iterator after it has already been initialized by another Iterator?

Solution

  • Isn't ConcurrentModificationException thrown when the list is being modified by two threads simultaneously ?

    Not necessarily. The ConcurrentModificationException indicates that the list has been structurally changed (except by the Iterator's own remove method) after the Iterator was created. This could be due to multiple threads using the same list, or it could be due to an attempt to for example remove items from an ArrayList inside a for each loop without using an Iterator.

    Does initializing the iterator, create a lock on the list ?

    No, there are no locks. When the Iterator is created it records the modCount of the ArrayList (a crude representation of the list's state that is incremented on every structural change). If an iterator detects a change to the List's modcount that wasn't caused by its own methods the exception is thrown.

    You are getting the exception from the second iterator because of the structural changes made to the list between the second iterator being instantiated and used.

    why does Java let us to initialize an Iterator after it has already been initialized by another Iterator?

    The ArrayList does not keep track of all the iterators that it has created, or their state. To do so would massively complicate the implementation. The modCount approach is not perfect and is a bit crude, but it is simple and identifies many real bugs.