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
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.
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.
TreeSet
.LinkedList
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With