7

I have read an article regarding removing elements from Collections from this link

As per my understanding iterator remove method prevents Concurrent modification exception then remove method of Collection.But when i try to run the below codde i am unable to get concurrentmoficationexception

     List dayList= new ArrayList();
     dayList.add("Sunday");
     dayList.add("Monday");
     dayList.add("Tuesday");
     dayList.add("Wednesday");
     dayList.remove("Tuesday");
     Iterator itr=dayList.iterator();
        while(itr.hasNext())
        {
           Object testList=itr.next();
           if(testList.equals("Monday"))
             {
            dayList.remove(testList);

             }
    }
    System.out.println(dayList);
}
  • As per javadoc the ConcurrentModicationException is thrown when we try to do any modification during iteartion.I am using collections remove method,but still there is no exception.But if I comment line dayList.remove("Tuesday");,exception is thrown.

Can anyone explain what is happening behind the scene in this code?

Sameer Kazi
  • 17,129
  • 2
  • 34
  • 46
coder25
  • 2,363
  • 12
  • 57
  • 104
  • Your description isn't accurate. You only get the exception if you uncomment both lines. – user207421 May 07 '14 at 07:09
  • Look at the ConcurrentModificationException documentation: http://docs.oracle.com/javase/7/docs/api/java/util/ConcurrentModificationException.html – Kuldeep Jain May 07 '14 at 07:11
  • @EJP Even if he keeps the `iterator.remove()` line commented out, he'll still get the exception when calling `iterator.next()` again. – Andrei Nicusan May 07 '14 at 07:11
  • the if(){ } is your saviour. Maybe the JVM thinks dayList.remove(testList); is safe :) . - "Fail-fast operations throw ConcurrentModificationException on a best-effort basis." – TheLostMind May 07 '14 at 07:16
  • I have edited the description of question.Can anyone please explain the fail-fast behaviour. – coder25 May 07 '14 at 07:53
  • @AndreiNicusan I tested his code and that's the only way I could get it to fail. – user207421 May 07 '14 at 09:15

3 Answers3

3

The fail-fast behavior of an iterator cannot be guaranteed. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. The iterator may or may not detect the invalid usage. 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.

Suren Raju
  • 3,012
  • 6
  • 26
  • 48
  • I would have liked to have seen the documentation specify that collections should endeavor to throw `ConcurrentModificationException` in cases where concurrent modification would make it impossible for an iterator to guarantee certain "sane" behavior, but that in cases where a collection could behave sanely there was nothing wrong with it doing so. – supercat Dec 12 '14 at 18:51
2

if I comment line dayList.remove("Tuesday");,exception is thrown....

Actually this is not problem here. Problem is exception is occurring for middle value only.

‘for each’ loop works as follows,

1.It gets the iterator.
2.Checks for hasNext().
public boolean hasNext() 
{
      return cursor != size(); // cursor is zero initially.
}
3.If true, gets the next element using next().

public E next() 
{
        checkForComodification();
        try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
        } catch (IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
        }
}

final void checkForComodification() 
{
    // Initially modCount = expectedModCount (our case 5)
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

Repeats steps 2 and 3 till hasNext() returns false.

In case if we remove an element from the list , it’s size gets reduced and modCount is increased.

If we remove an element while iterating, modCount != expectedModCount get satisfied and ConcurrentModificationException is thrown.

But removal of second last object is weird. Lets see how it works in your case.

Initially,

cursor = 0 size = 5 --> hasNext() succeeds and next() also succeeds without exception.
cursor = 1 size = 5 --> hasNext() succeeds and next() also succeeds without exception.
cursor = 2 size = 5 --> hasNext() succeeds and next() also succeeds without exception.
cursor = 3 size = 5 --> hasNext() succeeds and next() also succeeds without exception.

In your case as you remove ‘d’ , size gets reduced to 4.

cursor = 4 size = 4 --> hasNext() does not succeed and next() is skipped.

In other cases, ConcurrentModificationException will be thrown as modCount != expectedModCount.

In this case, this check does not take place.

If you try to print your element while iterating, only four entries will be printed. Last element is skipped.

Your question is similar to this question.

Community
  • 1
  • 1
Sameer Kazi
  • 17,129
  • 2
  • 34
  • 46
1

Well add a Thursday and you would start getting the exception again :)

List dayList= new ArrayList();
dayList.add("Sunday");
dayList.add("Monday");
dayList.add("Tuesday");
dayList.add("Wednesday");
dayList.add("Thursday");    // Added by Kuldeep
dayList.remove("Tuesday");
Iterator itr=dayList.iterator();
while(itr.hasNext())
{
    Object testList=itr.next();
    if(testList.equals("Monday"))
    {
        dayList.remove(testList);
    }
}
System.out.println(dayList);

So essentially that is because The fail-fast behavior of an iterator cannot be guaranteed. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Look at the documentation here.

EDIT : The exception is not thrown in your case is because in the following code:

    if(testList.equals("Monday"))
    {
        dayList.remove(testList);
    }

you are removing the second last element, which actually change the size or the dayList from 3 to 2. So the itr.hasNext() returns false after Monday is removed from the list and hence does not go into the loop for last entry i.e. Wednesday and avoids the call to itr.next(). You may try debugging the code to verify this behavior.

Kuldeep Jain
  • 8,409
  • 8
  • 48
  • 73
  • ConcurrentModificationException is thrown when the user tries to modify the Collection during iteration ,then why the code is not throwing exception and how does dayList.remove("Tuesday"); makes difference...the code inside while is sufficient to throw the exeption – coder25 May 07 '14 at 07:47
  • That is because `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.` Please read the documentation for ConcurrentModificationException. – Kuldeep Jain May 07 '14 at 08:09
  • I understood that but dayList.add("Wednesday"); and dayList.add("Thursday"); are done before iteration, how this is linked with exception and as per link http://javarevisited.blogspot.in/2014/01/ow-to-remove-objects-from-collection-arraylist-java-iterator-traversing.html ,it should throw exception in my case. – coder25 May 07 '14 at 08:18
  • Yes I debugged it, but why did the itr.hasNext() returned false ....after removing Monday,the list still has elements Sunday,Wednesday. – coder25 May 07 '14 at 09:25
  • and if I comment the line dayList.remove("Tuesday"),it throws exception at line Object testList=itr.next(); – coder25 May 07 '14 at 09:31
  • `itr.hasNext()` returns `false` because it checks for the size to be equal to cursor position (index of element to be returned by call to `next()` method)... And when you comment out call to dayList.remove("Tuesday") then `Monday` is the third last element so after "Monday" is removed then also control goes into the loop once for "Tuesday" and throws `ConcurrentModificationException` on the call to `Object testList=itr.next();` – Kuldeep Jain May 07 '14 at 10:12
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/52183/discussion-between-kuldeep-jain-and-prerna) – Kuldeep Jain May 07 '14 at 10:21