Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

It does not throw exception ConcurrentModificationException [duplicate]

Tags:

java

iterator

I have the below code and I would expect it to throw a ConcurrentModificationException, but it runs successfully. Why does this happen?

public void fun(){
    List <Integer>lis = new ArrayList<Integer>();
    lis.add(1);
    lis.add(2);

    for(Integer st:lis){
        lis.remove(1);
        System.out.println(lis.size());
    }
}

public static void main(String[] args) {
    test t = new test();
    t.fun();
}
like image 905
Alok Pathak Avatar asked Jul 03 '14 14:07

Alok Pathak


People also ask

How do I fix ConcurrentModificationException?

How do you fix Java's ConcurrentModificationException? There are two basic approaches: Do not make any changes to a collection while an Iterator loops through it. If you can't stop the underlying collection from being modified during iteration, create a clone of the target data structure and iterate through the clone.

What does ConcurrentModificationException mean?

The ConcurrentModificationException is used to fail-fast when something being iterated on is modified. This exception occurs when an object is attempted to be modified concurrently without permission.

What causes ConcurrentModificationException?

Class ConcurrentModificationException. This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible. For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it.

Which method throws ConcurrentModificationException?

If we invoke a sequence of methods on an object that violates its contract, then the object throws ConcurrentModificationException. For example: if while iterating over the collection, we directly try to modify that collection, then the given fail-fast iterator will throw this ConcurrentModificationException.


5 Answers

The remove(int) method on List removes the element at the specified position. Before you start your loop, your list looks like this:

[1, 2]

Then you start an iterator on the list:

[1, 2]
 ^

Your for loop then removes the element at position 1, which is the number 2:

[1]
 ^

The iterator, on the next implied hasNext() call, returns false, and the loop terminates.

You will get a ConcurrentModificationException if you add more elements to the list. Then the implicit next() will throw.

As a note, from the Javadoc for ArrayList from the JCF:

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. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

This is probably actually a bug in the Oracle ArrayList iterator implementation; hasNext() does not check for modification:

public boolean hasNext() {
    return cursor != size;
}
like image 115
chrylis -cautiouslyoptimistic- Avatar answered Oct 27 '22 12:10

chrylis -cautiouslyoptimistic-


It doesn't throw a ConcurrentModificationException because, as vandale said, the iterator only checks for comodification on next(). Here's a portion of the Iterator instance returned by an ArrayList:

    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];
    }

hasNext() simply looks to see if the cursor is pointing at the last index of the list. It doesn't check to see if the list was modified. Because of this you don't get an ConcurrentModificationException, it just stops iterating.

like image 29
Jason Nichols Avatar answered Oct 27 '22 11:10

Jason Nichols


if you have a list of 3 like:

lis.add(1);
lis.add(2);
lis.add(3);

you will get ConcurrentModificationException in your case. PS: I have tried this!

like image 40
Bill Lin Avatar answered Oct 27 '22 12:10

Bill Lin


Because you aren't removing 1, you are removing the element at 1. ( remove(int) vs remove(Object) )

The iterator will only check for modification on a call to next() and not hasNext(), and the loop will exit after the call to hasNext() because you have removed 2, the list is only one long and thus exits.

like image 27
vandale Avatar answered Oct 27 '22 11:10

vandale


The gist of the matter is, as stated on both ArrayList and ConcurrentModificationException:

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. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis.

Now a code sample from the Iterator returned by ArrayList:

    public boolean hasNext() {
        return cursor != size;
    }

    public E next() {
        checkForComodification();
        <stuff>
        return <things>;
    }

    <more methods>

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

As you can clearly see, in the case of ArrayList the "best effort" is in checking for modifications when calling next() and not when calling getNext(). Your loop terminates without calling next() a second time, hence no exception. Should you have 3 elements to begin with, or add an element it will fail. Also worth noting that if you modify the array list using reflection without updating the modCount variable (naughty...), then the exception will not be thrown at all. modCount is also not volatile, which again shows it's only best effort and has no guarantees, since the iterator might not see the latest value anyway.

like image 40
Ordous Avatar answered Oct 27 '22 12:10

Ordous