4

今日、HashSetのイテレータで奇妙な動作に遭遇しました。以下のコード例でidStringは、によって返されたオブジェクト参照を使用してhs.iterator、イテレータのnext()メソッドを呼び出します。

idString2イテレータではviaが呼び出され、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

これを、毎回使用する場合と比較してください。it毎回、単一のイテレータを使用するため、呼び出すたびに論理的な「カーソル」が進みますnext()

于 2012-06-12T20:13:35.227 に答える
3

これは、イテレータメソッドのJavadoc(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()生成することです。新しいイテレータは常に最初の要素を指します。したがって、メソッドでイテレータを使用する必要があります。itidString2

于 2012-06-12T20:14:27.920 に答える
0

これは、新しいイテレータインスタンスを取得し、イテレータに次の要素があるかどうかをチェックしている場合でも、whileが条件をチェックするたびに機能するためです。

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

常に最初の要素を指すため、常にtrueが返されますが、この行でイテレータのインスタンスがすでに割り当てられています。

イテレータit=hs.iterator();

したがって、すべての新しいイテレータインスタンスに次の要素があるかどうかを確認していても、it変数にのみ割り当てられた最初のイテレータインスタンスの次の要素を取得しています。

whileループは、「&& i ++ <= 10」条件のために終了するため、10回ループしてから、whileブロックの実行を停止します。

その条件が存在しない場合、イテレータの次の存在しない要素を取得しようとすると、NoSuchElementExceptionが発生します。

hasNext()は、次の要素があるかどうかのみをチェックしますが、next()は、呼び出されたイテレータオブジェクトに存在する場合、カーソルが次の要素を指すようにします。

于 2012-06-12T23:15:39.190 に答える