1

If I uncomment line list.add("3"), ConcurrentModificationException is thrown. For two elements it works fine and for 1 or 3 elements exception is thrown? Any explanation for this behavior?

import java.util.*;

public class ConException {

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        //list.add("3");
        for(String x: list){
            list.remove("1");
        }
        System.out.println(list);
    }
}
Kayaman
  • 72,141
  • 5
  • 83
  • 121

2 Answers2

0

You can't change list while iterating through it.
So you can't call list.remove("1") inside for (String x: list) loop.
Remove loop and simply remove "1" element.

If you want to remove all "1" elements you can do this by the following:

while (list.remove("1")) {}

EDIT:
Here is decompiled code of ArrayListIterator class, that is used in for-loops. After each iteration next method is called, where ConcurrentModificationException can be thrown

private class ArrayListIterator implements Iterator<E> {
    /** Number of elements remaining in this iteration */
    private int remaining = size;

    /** Index of element that remove() would remove, or -1 if no such elt */
    private int removalIndex = -1;

    /** The expected modCount value */
    private int expectedModCount = modCount;

    public boolean hasNext() {
        return remaining != 0;
    }

    @SuppressWarnings("unchecked") public E next() {
        ArrayList<E> ourList = ArrayList.this;
        int rem = remaining;
        if (ourList.modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        if (rem == 0) {
            throw new NoSuchElementException();
        }
        remaining = rem - 1;
        return (E) ourList.array[removalIndex = ourList.size - rem];
    }

    public void remove() {
        Object[] a = array;
        int removalIdx = removalIndex;
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        if (removalIdx < 0) {
            throw new IllegalStateException();
        }
        System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining);
        a[--size] = null;  // Prevent memory leak
        removalIndex = -1;
        expectedModCount = ++modCount;
    }
}

Our exception is thrown from next method in the following condition:

if (ourList.modCount != expectedModCount) {
    throw new ConcurrentModificationException();
}

And our modCount variable was changed with remove iteration, so the next next call will fail.

Artem Mostyaev
  • 3,874
  • 10
  • 53
  • 60
  • how about list.remove(list.indexOf(0) – bananas May 13 '16 at 06:35
  • @holidayCoder There are a lot of possible solutons :) Yours should work, but `list.remove(0)` will be easier to delete the first element. – Artem Mostyaev May 13 '16 at 06:38
  • The question is not about deleting the first element. It's about why `ConcurrentModificationException` isn't always thrown. – Kayaman May 13 '16 at 06:39
  • @Artem Mostyaev : I do know that I can't change the list in for loop. Thats was not the question. With two elements, when I run the above code, no exception is thrown. With one or three elements in list, exception is thrown. Why? – user6328922 May 13 '16 at 06:39
  • ConcurrentModificationException is thrown when we change a list inside a loop. And it crashes when trying to switch to the next element. So, if we have 2 elements, we need 2 switches and exceptions is not thrown (I don't know the exact for-loop implementation in Java). When we have 3 elements, we need 3 switches and crash possibility is higher. – Artem Mostyaev May 13 '16 at 06:42
  • @ArtemMostyaev You're right, you don't know the for-loop implementation. Which is why you shouldn't try to pretend to know what's happening. – Kayaman May 13 '16 at 06:44
  • he is getting string type in for loop, which has nothing to do with list? – bananas May 13 '16 at 06:47
  • basically he is getting array of string – bananas May 13 '16 at 06:47
  • @Kayaman Please see my last edit. Here is a decompiled implementation of for loops. – Artem Mostyaev May 13 '16 at 06:53
  • @ArtemMostyaev I know how for loops work. You should see the duplicate question about why it doesn't guarantee that a `CME` is thrown. Which was the whole point of the question. – Kayaman May 13 '16 at 06:56
  • @Kayaman Yes. I've seen at it. I wanted to say the same, but the explanation there is shorter and clearer :) – Artem Mostyaev May 13 '16 at 06:58
0

Please read the java doc of ConcurrentModificationException https://docs.oracle.com/javase/7/docs/api/java/util/ConcurrentModificationException.html

In your case, you DO NOT get the exception only when you have 2 elements in your ArrayList, in all other cases you get a ConcurrentModificationException.

As the javaDoc states it depends on JRE's determining if any concurrent modification is going on, and it is not a guaranteed way to determine same, as stated clearly in javadoc.

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

user1933888
  • 2,897
  • 3
  • 27
  • 36