Java HashSet的奇怪迭代器行为

时间:2012-06-12 20:09:34

标签: java iterator hashset

今天我用HashSet的迭代器遇到了一些奇怪的行为。 在下面的代码示例中,idString使用hs.iterator返回的对象引用来调用迭代器的next()方法。

idString2中,迭代器通过hs.iterator()调用,它不再起作用。

所以我假设HashSet.iterator()每次调用时都会返回一个新的迭代器对象。但是,为什么我仍然可以在while循环中使用hs.iterator().hasNext()

(请注意,下面的代码只是一个例子:))

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 个答案:

答案 0 :(得分:12)

每次调用iterator()时,它都会返回一个 new 迭代器,与之前创建的任何其他迭代器无关。因此,如果您致电hs.iterator().next() 总是为您提供第一个元素,如果您在非空集合上调用hs.iterator().hasNext(),它将始终 return true

每次使用it进行比较,它始终使用单个迭代器,因此每次调用next()时都会推进逻辑“光标”。

答案 1 :(得分:3)

iterator方法的Javadocs中没有明确记录这一点(无论是Collection还是Iterable接口),但所有Java集合总是在下返回一个新的迭代器iterator()调用。

因此,您应该重用您创建的迭代器,而不是在每次循环运行时重新创建迭代器。

例如, AbstractList 上有 iterator()实现:

/**
* 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's {@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's {@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();
}

答案 2 :(得分:1)

您的错误是HashSet.iterator()在每次调用时生成 new 迭代器。新迭代器始终指向第一个元素。因此,您必须在it方法中使用idString2迭代器。

答案 3 :(得分:0)

它的工作原理是因为即使你得到一个新的迭代器实例并检查迭代器上是否有下一个元素,每次while检查条件。

离。 while(hs.iterator()。hasNext()&amp;&amp; i ++&lt; = 10){..

它将始终返回true,因为它始终指向第一个元素,但是您已在此行中分配了迭代器的实例:

Iterator it = hs.iterator();

因此,即使您正在检查每个新迭代器实例中是否存在下一个元素,您将获得仅在该变量处分配的第一个迭代器实例中的下一个元素。

while循环由于“&amp;&amp; i ++&lt; = 10”条件而结束,因此它循环10次然后停止执行while块。

如果那个条件不存在,当你试图获得迭代器的下一个不存在的元素时,你会得到一个NoSuchElementException。

hasNext()仅检查是否存在下一个元素,而next()使光标指向下一个元素(如果它在迭代器对象上存在则被调用)。