Incorrect Java HashSet Iterator Behavior

Today I came across some weird behavior with a HashSet iterator. The idString code example below uses the object reference returned by hs.iterator to call the iterator next() method.

In idString2 iterator is called via hs.iterator() and it no longer works.

Therefore, I assume that HashSet.iterator () returns a new iterator object each time it is called. But why, why can I use hs.iterator().hasNext() in a while loop?

(Please note that the code below is just an example :))

 import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import org.junit.Test; public class DummyTest { static final HashSet<Integer> TEST_DATA = new HashSet<Integer>( Arrays.asList(new Integer[] { 1,2,3,4,5,6,7,8,9,10 })); @Test public void testRunTest() { // Correct output: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 System.out.println(idString(TEST_DATA)); // Only 1, 1, 1, 1, ... System.out.println(idString2(TEST_DATA)); } static String idString(HashSet<Integer> hs) { Iterator<Integer> it = hs.iterator(); String res = it.next() + ""; while (it.hasNext()) { res += ", " + it.next(); System.out.println(res); // debug } return res; } static String idString2(HashSet<Integer> hs) { Iterator<Integer> it = hs.iterator(); // Prevent an infinite loop int i = 0; String res = null; res = it.next() + ""; while (hs.iterator().hasNext() && i++ <= 10) { // if replacing hs.iterator() with 'it', it works res = res + ", " + hs.iterator().next(); System.out.println(res); // debug } return res; } } 
+4
source share
4 answers

Each time you call iterator() , it returns a new iterator, regardless of the other iterators created earlier. Therefore, if you call hs.iterator().next() , which always gives you the first element, and if you call hs.iterator().hasNext() in a non-empty collection, it will always return true .

Compare this to using it every time it uses one iterator everywhere, so every time you call next() , advance the logical "cursor".

+12
source

This is not clearly described in the Javadocs of the iterator method (whether in Collection or Iterable ), but all Java collections always return a new iterator under iterator () .

So, you should reuse the created iterator instead of re-creating the iterators each time the loop starts.

As an example, there is an implementation of iterator () in AbstractList :

 /** * Returns an iterator over the elements in this list in proper sequence. * * <p>This implementation returns a straightforward implementation of the * iterator interface, relying on the backing list {@code size()}, * {@code get(int)}, and {@code remove(int)} methods. * * <p>Note that the iterator returned by this method will throw an * {@link UnsupportedOperationException} in response to its * {@code remove} method unless the list {@code remove(int)} method is * overridden. * * <p>This implementation can be made to throw runtime exceptions in the * face of concurrent modification, as described in the specification * for the (protected) {@link #modCount} field. * * @return an iterator over the elements in this list in proper sequence */ public Iterator<E> iterator() { return new Itr(); } 
+3
source

Your mistake is that HashSet.iterator() generates a new iterator for each call. The new iterator always points to the first element. So you need to use it iterator in idString2 method.

+1
source

This works because, although you get a new instance of the iterator and check to see if there is another element on the iterator, the check is performed each time.

ex. while (hs.iterator (). hasNext () & i ++ <= 10) {..

it will always return the true reason, since it will always point to the first element, BUT you have already assigned an iterator instance on this line:

Iterator it = hs.iterator ();

That way, even if you check to see if there is a next element in each new iterator instance, you get the next element in the first iterator instance, only assigned in its variable.

The while loop ends due to the && ++ <= 10 condition, so it runs 10 times and then stops the execution of the while block.

If this condition was not met, you will get a NoSuchElementException when you try to get the next non-existent iterator element.

The hasNext () function checks if there is a next element, and next () makes the cursor a point of the next element, if it exists in the iterator object, was called in.

0
source

All Articles