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.
ConcurrentModificationException
thrown when the list is being modified by two threads simultaneously? Iterator
after it has already been initialized by another Iterator
?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.