1

If I write

String myFirstString = "a";
String mySecondString = "b";

List<String> lstOfStrings = new ArrayList<String>();
lstOfStrings.add(myFirstString);
lstOfStrings.add(mySecondString);

for (String value : lstOfStrings) {
    if(value.equals("a")) {
        lstOfStrings.remove("a");
        System.out.println("removed successfully");
    }
}

It works fine but,

If I change the order of insertion in list , it will gives java.util.ConcurrentModificationException see below code

String myFirstString = "a";
String mySecondString = "b";

List<String> lstOfStrings = new ArrayList<String>();
lstOfStrings.add(mySecondString);
lstOfStrings.add(myFirstString);

for (String value : lstOfStrings) {
    if(value.equals("a")) {
        lstOfStrings.remove("a");
        System.out.println("removed successfully");
    }
}

gives java.util.ConcurrentModificationException

Why such behavior occur in for each? I know there are many way like Iterator, CoppyOnWriteArraylist as resolution of ConcurrentModificationException Exception. but I want to know reason of this specific case. please explain.

Heejin
  • 4,463
  • 3
  • 26
  • 30
Jekin Kalariya
  • 3,475
  • 2
  • 20
  • 32
  • 1
    Google the error and you will get plenty of posts on this. – Erran Morad Jul 17 '14 at 06:09
  • possible duplicate of [Why isn't this code causing a ConcurrentModificationException?](http://stackoverflow.com/questions/14673653/why-isnt-this-code-causing-a-concurrentmodificationexception) – MK. Jul 17 '14 at 06:19

5 Answers5

3

A Java for-each loop internally uses iterator of the collection.

The behavior of Java iterator is fail-fast which fails when any contents of the collection has been changed while looping through it.

For more information, I recommend to read the articles below.

http://www.jguru.com/faq/view.jsp?EID=221988

http://www.developersfusion.com/Articles/AD/F/53/ConcurrentModificationException---Fail-Fast-and-Fail-Safe-iterators.aspx

http://docs.oracle.com/javase/6/docs/api/java/util/ConcurrentModificationException.html

Tugrul
  • 1,760
  • 4
  • 24
  • 39
Heejin
  • 4,463
  • 3
  • 26
  • 30
  • 2
    does not answer the question at all. The question is-- why does it happen in one example but not the other. – MK. Jul 17 '14 at 06:10
  • @MK Hmm, you are right. I missed the key point of the question. Your answer is an actual answer, and mine is only additional information. – Heejin Jul 17 '14 at 06:19
3

I am going to guess that the implementation of the byte-code generated for the iterator syntactic sugar of for (val:collection) includes an optimization of checking that you are on the last element of the collection and not bothering to enter the iterator again. So if you remove an item next to last in the collection it will not throw the exception and just skip the last item. This is somewhat proven by 2 experiments: add 3rd item to your first example and it will fail. Modify it to delete 2nd item instead of the 1st and will complete "successfully" again.

UPDATE: ah, this question is a duplicate of Why isn't this code causing a ConcurrentModificationException?

Community
  • 1
  • 1
MK.
  • 33,605
  • 18
  • 74
  • 111
1

It is because of the way that the ArrayList's iterator is implemented.

When you remove the element before the last one the iterator will not iterate the last one anymore.

Try printing out each iteration

String myFirstString = "a";
String mySecondString ="b";
List<String> lstOfStrings = new ArrayList<String>();

lstOfStrings.add(myFirstString);
lstOfStrings.add(mySecondString);

for (String value : lstOfStrings) {
    System.out.println("Iterating: " + value);

    if(value.equals("a")){
        lstOfStrings.remove("a");
        System.out.println("removed successfully");
    }
}

System.out.println(lstOfStrings);

The output is

Iterating: a
removed successfully
[b]

As you can see in the last System.out.printlnthe lstOfStringstill contains element "b", but it is not iterated.

And when you look at the Implementation of ArrayList you can see why.

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

The Iterator.hasNext() only checks if the actual cursor is not the size. So if you iterate through the list and remove string "a" the cursor points to the second element "b" but the size has been reduced to 1. So the second will not be iterated anymore.

This happens even in JDK 1.7 and I have not tested if this behavior still exists in 1.8.

When you use the Iterator.remove() method it will work, because the iterator is aware of the removal.

Iterator<String> iterator = lstOfStrings.iterator();
    while (iterator.hasNext()) {
        String value = iterator.next();
        System.out.println("Iterating: " + value);
        if (value.equals("a")) {
            iterator.remove();
            System.out.println("removed successfully");
        }
    }

Prints out:

Iterating: a
removed successfully
Iterating: b
René Link
  • 48,224
  • 13
  • 108
  • 140
1

There is no Exception in your first block of code snippet, because the loop will break out after the lstOfStrings.remove("a"), and before the interator.next() call. The iterator of the ArrayList is checking hasNext()by checking the return cursor != size, where the cursor is the current position in the iterator, that means after you remove the element: a from the list, the cursor increased: cursor++(cursor==1), but the size is decreased(you removed an element, size == 1). It does not throw Exception but it doesn't mean it is correct.

Think the following code:

    for (String value : lstOfStrings) {
    System.out.println("Iterating: " + value);
    if(value.equals("a")){
        lstOfStrings.remove("a");
        System.out.println("removed successfully");
    } else if (value.equals("b")) {
        doSomethingOnB(value) // this won't be executed anyway!!!
    }
}

And if you add one more elements into the lstOfStrings in the first code snippet, like: lstOfStrings.add("c"), it will throw the Exception you desire. :-)

The ConcurrentModificationException is thrown in your second block of code snippet, because it passed the hasNext() checking(cursor == 2, size == 1), then goes into the interator.next() call, which will check the modCount in the ArrayList and the expectedModCount in the Iterator, which represents count of modifications on the List elements size. Obviously, modeCount == 3 (2 adds and 1 remove), but the ArrayList.remove(Object) methods does not affect the expectedModCount in the ArrayList.Iterator, thus expectedModCount == 2, that is why it throws the Exception.

I am not sure whether it should be considered a bug in ArrayList, the root reason in your cases are the ArrayList's elements modification count is not synchronized with the Iterator's.

Lin Gao
  • 79
  • 1
  • 4
0

Some excerpts from ArrayList API -

  1. With every addition and removal on list modCount is incremented.

  2. For each loop's iterator call next() method for list size + 1 times in this case 3 times.

  3. When lstOfStrings.remove("a"); is called modCount is set to 3.

  4. At every next() iteration of iterator checkForComodification(); is called to check if there is any modification, since modification is found we get ConcurrentModificationException.

Why doesn't Iterator throw Exception then ?

Had there been call for checkForComodification(); after this ArrayList.this.remove(lastRet); It would have thrown same exception.

csarathe
  • 420
  • 1
  • 5
  • 12