I tried to reproduce ConcurrentModificationException myself by writing the following code:
List<String> last = new ArrayList<>();
last.add("a");
last.add("b");
for(String i : last){
System.out.println(i);
last.remove(i);
}
System.out.println(last);
DEMO
Since, the documentation of ArrayList mentioned
Note that the fail-fast behavior of an iterator cannot be guaranteed
as it is, generally speaking, impossible to make any hard guarantees
in the presence of unsynchronized concurrent modification.
I expected that in single-threaded programs such detection is straghtforward. But the program printed
a
[b]
instead. Why?
Your code is equivalent to the following:
List<String> last = new ArrayList<>();
last.add("a");
last.add("b");
for(Iterator<String> i = last.iterator(); i.hasNext(); ) {
String value = i.next();
System.out.println(value);
last.remove(value);
}
System.out.println(last);
The flow of the for loop is:
System.out.println(value); // prints "a"
last.remove(value); // removes "a" from the list
i.hasNext() // exits the loop, since i.hasNext() is false
System.out.println(last); // prints "[b]" - the updated list
That's why you get your output and no ConcurrentModificationException.
You will get ConcurrentModificationException if you'll add another value to your list (e.g. last.add("c")), because then i.hasNext() will be true after the first iteration and i.next() will throw the exception.
As explained in the documentation:
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
Because you are looping through the list using the new syntax for(String i : last) an iterator is created for you and you can't modify the list while looping it.
This exception is not related to multithreading. Also working with only one thread you can throw that exception.
Internally there is a variable modCount that is incremented for every modification to the structure of the list. When the iterator is first created it saves that value of modCount in a variable expectedModCount. Every subsequent modification check if that value of expectedModCount is equal to modCount. If not a ConcurrentModificationException is thrown.
I add the code of remove as an example. The same for add, addAll and all others methods that modify the list.
public E remove(int index) {
rangeCheck(index);
// Check if a modification should thrown a ConcurrentModificationException
checkForComodification();
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}
final void checkForComodification() {
if (expectedModCount != ArrayList.this.modCount)
throw new ConcurrentModificationException();
}
}
Itr class in ArrayList class has following methods
public boolean hasNext() {
return cursor != size;
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
Here, modCount is the number of times list has been structurally modified. When we create for loop, internally an iterator will be created and expectedModCount will be initialized to modCount.
When there are only 2 elements in the list and after removing one element, for loop will check the condition using hasNext() method call. So, condition cursor != size (1!=1) will be met first. Hence, loop won't proceed further and ConcurrentModificationException will not be thrown.
But, when there are 1,3,4 etc number of elements are there in the list then, for loop will proceed further after hasNext() method call. But, while fetching the element using next() method inside for loop, it will call checkForComodification() and condition modCount != expectedModCount will be met. Hence, exception will be thrown.
For two values, it will not fail because the for loop will be exited. Add a third element and you will get java.util.ConcurrentModificationException
List<String> last = new ArrayList<>();
last.add("a");
last.add("b");
last.add("c");
for(String i : last){
System.out.println(i);
last.remove(i);
}
System.out.println(last);
Related
This question already has an answer here:
ConcurrentModificationException not thrown consistently
(1 answer)
Closed 1 year ago.
G'day everyone. Today I get a quite weird situations regarding ConcurentModificationException.
I have a list like this.
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
I also have three pieces of code like this.
This will print [2, 3].
for (int i = 0; i < list.size(); i++) {
Integer integer = list.get(i);
if (integer.equals(1)) {
list.remove(integer);
}
}
System.out.println(list);
This will throw exception.
for (Integer integer : list) {
if (integer.equals(1)) {
list.remove(integer);
}
}
System.out.println(list);
This will print [1, 3].
for (Integer integer : list) {
if (integer.equals(2)) {
list.remove(integer);
}
}
System.out.println(list);
I used to think that removing an element inside a for-loop always results ConcurrentModificationException but I might be wrong. Can you guys tell me what create the difference here.
I run the code on Corretto 11.
The point here is that the ConcurrentModificationException is thrown when you access(!) the next element via the iterator, not when checking for the existance of a further element.
In the second snippet you remove the first element then there are still two to go. For the next iteration it will be noticed, that there are more elements which will be accessed and as the expectedModCount changed, the Exception will be thrown.
In the third example however, due to the reduced size, you will have reached the end after the second iteration and no further access will take place and therefore no exception is thrown.
The first example is not meant to throw an exception, because you are not using an iterator, which would check for the concurrent modification in the first place.
As was mentioned earlier first code snippet is not supposed to throw ConcurrentModificationException, because you are not iterating over list.
As regards to other two snippets the equivalent code is following:
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
Iterator it = list.iterator();
while(it.hasNext()) {
Integer integer = (Integer)it.next();
if (integer.equals(2)) {
list.remove(integer);
}
}
System.out.println(list);
and if you take a look at java.util.ArrayList.Itr implementation:
public boolean hasNext() {
return cursor != size;
}
#SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
you can find out that the reason of such behaviour is caused by cursor = i + 1 in next() call: removing penultimate element in ArrayList decreases size and followed hasNext() call returns false.
A regular for loop and an enhanced one are, slightly, different.
While the regular one loops over the list and gets the individual elements one by one, the enhanced one uses an Iterator. The enhanced for loop works with Iterable objects or an array.
Basically the enhanced for loop translates to this
for(Iterator<Integer> it = list.iterator(); it.hasNext(); ) {
Integer integer = it.next();
if (integer.equals(2)) {
list.remove(integer);
}
}
This also explains why the first enhanced loop throws an exception. As according to the iterator there should be more elements. Calling the next method detects that the underlying collection has changed and thus throws a ConcurrentModificationException.
The last one doesn't because you are already at the end of the iterator and thus no additional call to next.
When you remove an element at index 0, the list shrinks, therefore your list with [1,2,3] is now [2,3]. you then remove index 1 wich remove the 3. You have now [2] in your list. Then you remove index 2 which no longer exist.
You may want to remove only index 0 at every iteration or remove from the last index to the first.
intLinkList = 2, 4, 6, 8, 10
Iterator itr = intLinkList.iterator()
Say the iterator is iterating and currently pointing at Integer 6.
itr current item = 6
itr previous item = 4
itr next item = 8
When the itr is currently pointing at Integer 6, I use the Linklists' add(Object obj, int index) method to add/insert Integer 7 between Integer 6 and Integer 8.
I understand this itr instance is invalid after this modification because the underlying list has been modified, hence modCount != expectedModCount.
My question is this:
Does the modification using LinkedList's add method change the item the itr.next is pointing at?
I did some reading and I know this will throw a ConcurrentModificationException.
But this does not answer my question if the itr.next item is modified if the underlying list get modified while iterator is iterating.
Does the modification using LinkedList's add method change the item the itr.next is pointing at?
No.
Calling LinkedList's add doesn't change anything in the Iterator's state. Once you call, the Iterator's next() method, and before the iterator calculates the next element to return, it will check for modification and throw the ConcurrentModificationException.
Here's the relevant code, from AbstractList$Itr:
public E next() {
checkForComodification(); // will throw ConcurrentModificationException
// before the Iterator's state is changed
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
A key detail is that Java only has primitives and references.
When you add something to a List or any collection, it is a copy of the reference.
If you modify the object referenced, the collection is not modified though if you print the contents it might appear so.
If you call add or remove on a collection, for LinkedList and ArrayList, the iterator isn't modified but can no longer iterate (there is one exception)
If you use a CopyOnWriteArrayList you can modify the collection and keep iterating, however the iterator doesn't see the change.
This question already has answers here:
ConcurrentModificationException using Iterator
(2 answers)
Closed 5 years ago.
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.
Isn't ConcurrentModificationException thrown when the list is being modified by two threads simultaneously?
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?
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.
You have to load the second iterator after you have used the first iterator. Otherwise the second iterator "thinks" the list hasn't been changed but in reality it did. Because the list got changed the second iterator react like "Wait a minute, that shouldn't be here/gone" and throws a ConcurrentModificationException.
It let you initialize the iterator at any time. When you don't change the content you might be even fine with it and you don't get a ConcurrentModificationException because nothing has been changed.
A ConcurrentModificationException may be thrown whenever you try to use an invalid iterator - which can happen whenever you create an iterator and then modify the underlying collection from a different access point. Here, lit is initialized and then the list is modified through it, so its invalidated, which explains the exception.
ListIterator throws ConcurrentModificationException it there is a modification in list after its creation. In your code you have created Iterator and ListIterator at the same time an later you are deleting something from the list which causes ConcurrentModificationException.
To avoid this changes your code to below one. You just need to move the ListIterator initialization after the operations of iterator.
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();
while (it.hasNext()) {
String s = it.next();
if (s.startsWith("a")) {
it.remove();
} else {
System.out.println(s);
}
}
System.out.println(list);
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
String s = lit.next();
if (s.startsWith("a")) {
lit.set("1111" + s);
} else {
System.out.println(s);
}
}
System.out.println(list);
}
I have a very basic question.
I have created simple ArrayList and I am removing the item while iterating using for-each loop. It gives me java.util.ConcurrentModificationException because I can't remove an item while iterating but when I un-comment the if condition it works fine.
Please can anybody explain me how for-each works in this way.
ArrayList<String> list1 = new ArrayList<String>();
list1.add("Hello");
list1.add("World");
list1.add("Good Evening");
for (String s : list1) {
//if (s.equals("World")) {
list1.remove(1);
//}
}
If I change it to list1.remove(2); or list1.remove(0); then also its working fine.
Note: This is sample code and I know it will work fine using Iterator. My sole purpose of this question is to know how method remove() works perfectly if condition is un-commented no matter what index you are removing from the list.
The list has a variable called modCount, which means "modification count". Whenever you call remove (or perform other structural modifications), it increments the modCount.
The iterator can't keep track of its position in the list if you are adding or removing elements without telling the iterator. So as a safety check, at the start of iteration, the iterator makes a note of the modCount, saving it as expectedModCount. When each item is read from the iterator, the iterator checks to make sure the modCount still equals the expected value, and throws an exception if it doesn't.
Usually, this will successfully cause the exception to be thrown if the list is unsafely modified during iteration. However, it's not sufficient in this case when the if statement is enabled. After your code has read "World", that item is removed, and so the list now contains ["Hello", Good Evening"]. The iterator is still at position 1 (which now contains "Good Evening") and when it tries to read the next item, it finds it has now reached the end of the list, so it doesn't bother to check the modCount. Hence, no exception.
Note the caveat in the ConcurrentModificationException documentation: "It is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast operations throw ConcurrentModificationException on a best-effort basis."
Even if it doesn't happen to throw the exception in this case, the code is still wrong. To remove an element while iterating, you must use the iterator's own remove method:
for (Iterator<String> it = list1.iterator(); it.hasNext();) {
String s = it.next();
if (s.equals("World")) {
it.remove();
}
}
That way, the iterator knows that the list has changed and can still iterate correctly.
Alternatively, you can iterate from a temporary copy of the list:
for (String s : new ArrayList<>(list1)) {
if (s.equals("World")) {
list1.remove(...);
}
}
Although, in this simple case, you don't even need to do that; you can just write:
list1.remove("World");
You can also use an index-based removal. The drawback of this solution is that the list1.size() gets evaluated during every loop iteration. The positive thing is that removing an item from a List by its index is faster.
for (int i = 0; i < list1.size(); /* i incremented in loop body */) {
if ("World".equals(list1.get(i))) {
list1.remove(i);
}
else {
i++;
}
}
Use an Iterator and call remove():
Iterator<String> iter = list1.iterator();
while (iter.hasNext()) {
String str = iter.next();
if (someCondition)
iter.remove();
}
This question already has answers here:
Why is a ConcurrentModificationException thrown and how to debug it
(8 answers)
java.util.ConcurrentModificationException not thrown when expected
(2 answers)
Closed 3 years ago.
I was reading about ConcurrentModificationException and how to avoid it. Found an article. The first listing in that article had code similar to the following, which would apparently cause the exception:
List<String> myList = new ArrayList<String>();
myList.add("January");
myList.add("February");
myList.add("March");
Iterator<String> it = myList.iterator();
while(it.hasNext())
{
String item = it.next();
if("February".equals(item))
{
myList.remove(item);
}
}
for (String item : myList)
{
System.out.println(item);
}
Then it went on to explain how to solve the problem with various suggestions.
When I tried to reproduce it, I didn't get the exception! Why am I not getting the exception?
According to the Java API docs Iterator.hasNext does not throw a ConcurrentModificationException.
After checking "January" and "February" you remove one element from the list. Calling it.hasNext() does not throw a ConcurrentModificationException but returns false. Thus your code exits cleanly. The last String however is never checked. If you add "April" to the list you get the Exception as expected.
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
public class Main {
public static void main(String args[]) {
List<String> myList = new ArrayList<String>();
myList.add("January");
myList.add("February");
myList.add("March");
myList.add("April");
Iterator<String> it = myList.iterator();
while(it.hasNext())
{
String item = it.next();
System.out.println("Checking: " + item);
if("February".equals(item))
{
myList.remove(item);
}
}
for (String item : myList)
{
System.out.println(item);
}
}
}
http://ideone.com/VKhHWN
From ArrayList source (JDK 1.7):
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
#SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
Every modifying operation on an ArrayList increments the modCount field (the number of times the list has been modified since creation).
When an iterator is created, it stores the current value of modCount into expectedModCount. The logic is:
if the list isn't modified at all during the iteration, modCount == expectedModCount
if the list is modified by the iterator's own remove() method, modCount is incremented, but expectedModCount gets incremented as well, thus modCount == expectedModCount still holds
if some other method (or even some other iterator instance) modifies the list, modCount gets incremented, therefore modCount != expectedModCount, which results in ConcurrentModificationException
However, as you can see from the source, the check isn't performed in hasNext() method, only in next(). The hasNext() method also only compares the current index with the list size. When you removed the second-to-last element from the list ("February"), this resulted that the following call of hasNext() simply returned false and terminated the iteration before the CME could have been thrown.
However, if you removed any element other than second-to-last, the exception would have been thrown.
I think the correct explanation is this extract from the javadocs of ConcurrentModificationExcetion:
Note that fail-fast behavior cannot be guaranteed as it is, generally
speaking, impossible to make any hard guarantees in the presence of
unsynchronized concurrent modification. Fail-fast operations throw
ConcurrentModificationException on a best-effort basis. Therefore, it
would be wrong to write a program that depended on this exception for
its correctness: ConcurrentModificationException should be used only
to detect bugs.
So, if the iterator is fail fast it may throw the exception but thereĀ“s no guarantee. Try replacing February with January in your example and the exception is thrown (At least in my environment)
The iterator checks that it has iterated as many times as you have elements left to see it has reached the end before it checks for a concurrent modification. This means if you remove only the second last element you don't see a CME in the same iterator.