Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modify set of Intger vs set of String while Iterating [duplicate]

Tags:

java

iterator

set

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.

like image 908
AmolG Avatar asked Mar 03 '23 00:03

AmolG


1 Answers

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");
}
like image 81
Eran Avatar answered Mar 05 '23 14:03

Eran