Reputation: 17444
The code is pretty simple:
List lst1 = new ArrayList(){{add("1");}};
Iterator it = lst1.iterator();
System.out.println(it.hasNext());
it.next();
System.out.println(it.hasNext());
lst1.remove(0);
System.out.println(it.hasNext());
I know that you should never remove from a collection while iterating through its iterator, please don't tell me that.
Instead, please give me a sane explanation why would a remove() operation change the value returned by hasNext() from false to true:
$ /usr/lib/jvm/sun-jdk-1.6/bin/java -version
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) Server VM (build 20.1-b02, mixed mode)
true
false
true
hasNext() API documentation clearly states that it should return true ONLY if there are more elements in the iterator, and calling next() returns an element. The output proves that it's not the case with SUN JDK 1.6
IBM JDK doesn't do that, hasNext returns false after remove() gets called:
$ /usr/lib/jvm/ibm-jdk-bin-1.6/bin/java -version
java version "1.6.0"
Java(TM) SE Runtime Environment (build pxi3260sr9fp2-20110625_01(SR9 FP2))
IBM J9 VM (build 2.4, JRE 1.6.0 IBM J9 2.4 Linux x86-32 jvmxi3260sr9-20110624_85526 (JIT enabled, AOT enabled)
true
false
false
Is there a bug in Sun/Oracle hasNext() method? Or, is there a bug in IBM hasNext() method? Or, should I just not bother with all that?
Upvotes: 1
Views: 3592
Reputation: 38265
To really answer your question (why this happens), you need to look in the java.util.AbstractList.Itr
inner class, which is returned by the iterator
method of AbstractList
(ArrayList
does not overwrite that method). The implementation of hasNext
there looks like this:
public boolean hasNext() {
return cursor != size();
}
Your remove()
call changed the value returned by size()
but did not modify the cursor
value hold by the Itr
instance, so the two values are now different.
If they would have performed a "less than" check instead of !=
, the returned value would have been what you expected, but they assumed !=
can only mean "less than", unless you modify the collection from outside, which is "illegal"; and in this case, any subsequent call to next()
would result in an exception anyway.
Upvotes: 4
Reputation: 269658
The documentation is clear:
The behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling [the
remove
] method.
The important word here is unspecified. This means that once you've modified the underlying collection then the iterator can do anything! hasNext
might return true
; it might return false
; it might throw an exception; etc, etc.
Upvotes: 3
Reputation: 425448
While iterating over a regular Collection, you must use Iterator.remove();
to remove the current element. Any attempt at modification (adding or removing) of the Collection while iterating will throw an ConcurrentModificationException
.
However, there are special Collections that allow concurrent modification to the Collection while iterating, such as CopyOnWriteArrayList et al, but element-changing operations on iterators themselves (remove, set, and add) are not supported (these methods throw UnsupportedOperationException).
Upvotes: 5
Reputation: 5341
From Iterator.remove(): "The behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling this method."
So it is unspecified, meaning that NOTHING is a bug. It could format your harddisk in this case, and still not be incorrect.
Upvotes: 2