12

I have below two java class

import java.util.*;

public class ArrayListTest032 {
    public static void main(String[] ar) {
        List<String> list = new ArrayList<String>();
        list.add("core java");
        list.add("php");
        list.add("j2ee");
        list.add("struts");
        list.add("hibernate");

        Iterator<String> itr = list.iterator();

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
        list.remove("php");

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }

    }
}

When I run above code I get below output.

core java
php
j2ee
struts
hibernate

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
    at java.util.AbstractList$Itr.next(AbstractList.java:343)
    at ArrayListTest032.main(ArrayListTest032.java:20)

Which is expected as I am modifying the list while iterating. But in below java class same logic is executed by set family.

import java.util.*;

public class HashSetTest021 {
    public static void main(String[] ar) {
        Set<String> set = new HashSet<String>();
        set.add("core java");
        set.add("php");
        set.add("j2ee");
        set.add("struts");
        set.add("hibernate");

        Iterator<String> itr = set.iterator();

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
        set.remove("php");

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }

    }
}

And out put is.

hibernate
core java
j2ee
php
struts

There is no any ConcurrentModificationException.

I just want to know why same piece of code throws ConcurrentModificationException in case of list family, but there is no any ConcurrentModificationException in case of set family

Rais Alam
  • 6,970
  • 12
  • 53
  • 84

6 Answers6

8

This is a kind of 'retrograde' behavior, insomuch as iterators, once fully traversed, are not reusable, aka their hasNext method should return false when you reach the end of the list.

In this case though, the iterator returned by ArrayList.iterator is an internal implementation class, with code for hasNext as follows:

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

So when you call hasNext in your second loop, it indicates (falsely) that there are more items to iterate over, because you executed an operation that changed the size of the list, after the first iteration. Semantically, you should not be able to continue iterating over items in the list after you reach the end of it, but due to this implementation detail it lets you proceed with the second while loop. Of course, at that point, you get a concurrent modification exception because of the change you made in the backing list.

On the other hand, the iterator used by your hash set has its hasNext implemented as follows:

public final boolean hasNext() {
    return next != null;
}

This implementation happens not to be as 'vulnerable' to modifications made to the hash set after an iteration has been completed, and as such the hasNext method is better behaved.

Perception
  • 79,279
  • 19
  • 185
  • 195
4

This is a difference in the implementation: the iterator returned by the array list detects concurrent modifications even when it is positioned at the end, because it checks the length; iterators of the HashSet, TreeSet and LinkedList, on the other hand, do not detect this condition, because they check for being positioned at the end before checking for concurrent modification. The documentation allows iterators not to throw on concurrent modifications, so both approaches are valid.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • nit: the ArrayList iterator actually checks a modification count, not the length per se, so if you add then immediately remove an element, leaving the length the same, you'll still get a ConcurrentModificationException afterwards. – Alice Purcell Dec 07 '13 at 11:34
2

Start by reading the JavaDoc for Iterator. Does it mention ConcurrentModificationException anywhere?

Now, read the JavaDoc for ConcurrentModificationException, and note the following (emphasis added):

This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.

Now look closely at your code. Your while loop iterates through all elements of the collection (even though the output of your first example doesn't indicate this, which tells me that either you've edited the output or this is not your actual code). At the time you remove the element, there are no more items to iterate, so the second loop should always exit immediately.

So, the conclusion is that the implementers of the list iterator have chosen that to throw that exception even when there are no more elements to iterate, while the implementers of the set iterator have chosen not to. Both cases are completely acceptable given the specifications.

parsifal
  • 529
  • 2
  • 4
  • I have added same class in above code you can run the code and check the output yourself. – Rais Alam Feb 26 '13 at 14:56
  • @Real - yes, I ran it, and the output showed "hibernate", which your posting does not. But really, rather than getting your shorts in a twist about a parenthetical comment, you should actually think about the *content* of my answer. – parsifal Feb 26 '13 at 15:09
  • Any idea why this design in which list throws and set doesn't was preferred? – djechlin Feb 26 '13 at 19:36
  • @djechlin - It's not design, it's implementation. I originally commented that it was probably just preference on the part of the implementer, but I think that *Perception* has a good explanation (although I remember more code in the implementations when I last looked at them several years ago). – parsifal Feb 26 '13 at 19:56
  • But the key point of my answer is that the observed behavior is all according to spec, and that as such there's no good reason to question it. And that the likely reason that it's being questioned is that the OP was depending on unspecified behavior. – parsifal Feb 26 '13 at 19:57
1
 public static void main(String[] ar) {
            List<String> list = new ArrayList<String>();
            list.add("core java");
            list.add("php");
            list.add("j2ee");
            list.add("struts");
            list.add("hibernate");

            Iterator<String> itr = list.iterator();

            while (itr.hasNext()) {
                System.out.println(itr.next());
            }
            list.remove("php");

          /*  while (itr.hasNext()) {
                System.out.println(itr.next());
            }*/

        }

problem in itr object.it holds the list object reference
Biswajit
  • 2,434
  • 2
  • 28
  • 35
  • then why set do not throws exception. you can check both class – Rais Alam Feb 26 '13 at 14:50
  • From the output stack trace, its clear that the exception is coming when we call iterator next() function. If you are wondering how Iterator checks for the modification, its implementation is present in AbstractList class where an int variable modCount is defined that provides the number of times list size has been changed. – Biswajit Feb 26 '13 at 14:58
1

Hashset can throw a ConcurrentModificationException if you do anything to the set except through the iterator. However, there are a lot of heuristics around the itertor's fast-fail behavior with the goal to complete the iteration if at all possible. The JavaDocs seem pretty clear on it's behavior.

WPrecht
  • 1,340
  • 1
  • 17
  • 29
0

In case of list when we traverse it with first loop Iterator itr = set.iterator();

    while (itr.hasNext()) {
        System.out.println(itr.next());
    }

The cursor value and size will become same.Cursor contains value for total no of elements traversed and inside hashNext() method for list traversal contains code as:

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

So after first while loop cursor == size.But after removing element from list size becomes (originalSize-1).So for next while loop it goes inside while and inside itr.next() method it checks for modcount modification and throws ConcurrentModificationException.

In case of Set it checks for next != null for every itr.hasnext() call.And after traversing first while loop next becomes null.Removing element from set does not affect next value as null and itr.hasNext will return next == null as true and hence it does not go inside while loop to check modcount modification.And hence it does not throw ConcurrentModification Exception.

Nitin Pawar
  • 1,634
  • 19
  • 14