4

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

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

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

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

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

4 回答 4

12

每次调用iterator()它都会返回一个的迭代器,独立于之前创建的任何其他迭代器。所以如果你调用hs.iterator().next()总是会给你第一个元素,如果你调用hs.iterator().hasNext()一个非空集合,它总是会返回true

将其与 using iteach time 进行比较,后者始终使用单个迭代器,因此每次调用时都会推进逻辑“光标” next()

于 2012-06-12T20:13:35.227 回答
3

这在迭代器方法的 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();
}
于 2012-06-12T20:13:35.743 回答
1

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

于 2012-06-12T20:14:27.920 回答
0

它之所以有效,是因为即使您正在获取一个新的迭代器实例并检查迭代器上是否有下一个元素,每次 while 都会检查条件。

前任。而 (hs.iterator().hasNext() && i++ <= 10) {..

它总是返回 true 因为它总是指向第一个元素,但是你已经在这一行中分配了一个迭代器的实例:

迭代器它 = hs.iterator();

因此,即使您正在检查每个新迭代器实例中是否有下一个元素,您也会得到第一个迭代器实例中的下一个元素,仅分配给 it 变量。

while 循环由于 "&& i++ <= 10" 条件而结束,因此它循环了 10 次,然后停止执行 while 块。

如果该条件不存在,当您尝试获取迭代器的下一个不存在的元素时,您将收到 NoSuchElementException。

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

于 2012-06-12T23:15:39.190 回答