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.