4

I have following two classes of codes :

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class TestSet {

public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        set.add(90);
        set.add(10);
        set.add(20);
        set.add(30);
        System.out.println(set);
        Iterator<Integer> itr = set.iterator();
        while(itr.hasNext()){
            Integer ob = itr.next();
            if(ob.equals(10)){
                set.add(11);
            }
        }
        System.out.println(set);
    }

}

output of above code is

[20, 90, 10, 30]
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
    at java.util.HashMap$KeyIterator.next(HashMap.java:1466)
    at com.collection.TestSet.main(TestSet.java:18)

and another class

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class TestIntegerSet {

    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("90");
        set.add("10");
        set.add("20");
        set.add("30");
        System.out.println(set);
        Iterator<String> itr = set.iterator();
        while(itr.hasNext()){
            String ob = itr.next();
            if(ob.equals("10")){
                set.add("11");
            }
          }
        System.out.println(set);
    }

}

while output of above code is

[90, 30, 20, 10]
[11, 90, 30, 20, 10]

I am not able to understand why there is strange behavior? Or I am doing something wrong?

I am trying to iterate using iterator but why its throwing concurrent modification exception for integer, while its showing proper value for String.

Youcef LAIDANI
  • 55,661
  • 15
  • 90
  • 140
AmolG
  • 53
  • 8

3 Answers3

4

Both snippets are wrong. You should not make structural changes in a Collection while iterating over it (to be exact, the only structural change you are allowed to make on the Set during iteration is via the Iterator's remove() method).

The fact that one snippet throws ConcurrentModificationException and the other doesn't is an artifact of the implementation of HashSet.

The iteration order of the elements of a HashSet is an implementation detail that depends on the hashCode() of the elements. Integers and String representations of those same Integers have different hashCodes, and therefore the iteration order (as well as the location of the last added element within the order of iteration) is different for the two snippets.

The JDK detects concurrent modification and throws ConcurrentModificationException whenever it can, but there are cases (like your second snippet) where it fails to do so.

If you change your first snippet to:

Set<Integer> set = new HashSet<>();
set.add(90);
set.add(10);
set.add(20);
set.add(30);

System.out.println(set);
Iterator<Integer> itr = set.iterator();
while(itr.hasNext()){
    Integer ob = itr.next();
    if(ob.equals(30)){
        set.add(11);
    }
}
System.out.println(set);

it won't throw an exception, since 11 will now be added after all the original 4 elements are iterated, so the Iterator won't get a chance to throw an exception before iteration ends.

Similarly, the second snippet, that currently seems to work, will throw an exception if you change the condition from

if(ob.equals("10")){
    set.add("11");
}

to

if(ob.equals("20")){
    set.add("11");
}
Eran
  • 387,369
  • 54
  • 702
  • 768
2

It's because in case of String, "10" is the last element to iterate, so the execution comes out of the loop. But in case of Integer it's not the last element to iterate, so on the next iteration, java detects that the Set is changed and hence throws the exception. As you know that the iteration of Set is not as per the insertion order, so it's different in case of String and Integer.

Analyze the output, for better clarity. printing the objects inside whileloop

[90, 30, 20, 10]
inside string set - 90
inside string set - 30
inside string set - 20
inside string set - 10
[11, 90, 30, 20, 10]
[20, 90, 10, 30]
inside integer set - 20
inside integer set - 90
inside integer set - 10
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
    at java.util.HashMap$KeyIterator.next(HashMap.java:1466)
    at Test.main(Test.java:41)
BHAWANI SINGH
  • 729
  • 4
  • 8
1

@Eran' answer explain what happen correctly, to avoid your issue, you don't need to use an Iterator at all, you just this :

if(set.contains(10)){
    set.add(11);
}
Youcef LAIDANI
  • 55,661
  • 15
  • 90
  • 140