2

Look at this little piece of code:

ArrayList al = new ArrayList();
al.add("AA");
al.add("AB");
al.add("AC");
Iterator it = al.iterator();
while(it.hasNext()){
String s = (String)it.next();
if(s.equals("AB")){
al.remove(1);
}
}

Since ArrayList has fail-fast iterator, and clearly, the given ArrayList isn't made of fixed size arrays (which would render the remove() method unusable), the above code should have thrown ConcurrentModificationException but yes, it does not.

Also, if I insert a print statement in the loop (as the first statement), it shows that the loop does not iterate the third time and that it exits gracefully.

I know it sounds too stupid but the only reason I could think of falsely is the fact that the removal of the element happens after the element has been traversed by the iterator. But that just cannot be the case as the modificationCount is still modified by removal and so it must throw the Exception.

Just doing

while(it.hasNext()){
it.next();
al.remove(1);
}

does throw ConcurrentModificationException though.

Any insights?

halfer
  • 19,824
  • 17
  • 99
  • 186
Aakash Verma
  • 3,705
  • 5
  • 29
  • 66

2 Answers2

2

It happens because the hasNext() method does not check the modCount:

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

So, after calling remove(1), the size of the list will be 2 just like the cursor, and hasNext() will return false. The next() method will never be called and modCount will never be checked.

If you add a fourth element to the list before iterating, you will get the exception similarly to your second example.

Bubletan
  • 3,833
  • 6
  • 25
  • 33
0

The check for concurrent modification happens only during an Iterator's next() call but not within its hasNext() call as explained in Bubletan's answer.

The java documentation for ArrayList clearly specifies that,

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.

So, it's a wrong programming practice to modify a collection while it is being iterated.

Pallavi Sonal
  • 3,661
  • 1
  • 15
  • 19
  • Is the best-effort basis this bad that a change in a very simple flow remains undetected in all the executions I have carried out? I had believed that there is some other kind of signal handling thread that takes care of listening to modifications. From what you are telling me, if I try to remove the last element while traversing using an iterator, there'll be never an exception thrown because of the absence of any more next() calls, isn't it? – Aakash Verma Jul 29 '17 at 11:12
  • Bubletan's answer clears it all. It really shouldn't be called a best effort basis :D Thank you! – Aakash Verma Jul 29 '17 at 11:20