2014-01-12 12 views
5

イテレータの反復回数をカウンタとして使用しようとしていますが、それを行うことの効果が不思議でした。イテレータは反復処理中のコレクションを変更できますか? Java

private int length(Iterator<?> it) { 
    int i = 0; 

    while(it.hasNext()) { 
     it.next(); 
     i++; 
    } 

    return i; 
} 

これはうまくいきますが、イテレータが背後で何をする可能性があるか心配です。おそらく、私はスタックを繰り返しているので、スタックからアイテムをポップしたり、優先キューを使用している場合は優先順位を変更したりします。

javadocツールがイテレータについてはこれを言う:


Eは、次の()
繰り返し処理で次の要素を返し 。
戻り値:
反復
の次の要素例外:
はNoSuchElementExceptionを - の繰り返しがそれ以上の要素を持っていない場合

私は変更されません。この未知のコレクションを反復処理保証が表示されませんそれ。私は非現実的なケースを考えていますか、それとも懸念していますか?より良い方法がありますか?

+0

誰かがあなたに 'Iterator'を渡している場合、彼らは副作用が何であれ、' next() 'を呼び出すことを期待しています。 –

答えて

5

Iteratorは、単にので、それは何らかの方法でデータを破壊するnext()のために完全に可能であるが、それはユニークでかけがえのないことをIteratorのデータに対しても可能ですだけではなく、ストリームのいくつかの並べ替えにインターフェースを提供します。

もっと直接的な例を考え出すことができますが、簡単な例はIteratorDirectoryStreamです。

Path dir = ... 
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { 
    int count = length(stream.iterator()); 
    for (Path entry: stream) { 
    ... 
    } 
} 

ストリームは一度だけ繰り返すことができるので、あなたは、foreachのブロックで例外になるだろう:DirectoryStreamは技術的にIterableですが、あなたが次の操作を実行しようとしたのであれば、それだけで、1 Iteratorを構築することを可能にします。要約すると、length()メソッドはオブジェクトを変更してデータを失う可能性があります。

さらに、Iteratorに別のデータストアを関連付けなければならない理由はありません。たとえばanswer I gave a few monthsのように、n乱数を選択するクリーンな方法を提供します。無限のIteratorを使用することにより、任意の大量のランダムデータを遅延して一挙に格納したり、必要になるまで計算したりする必要はありません。 Iteratorはデータ構造を戻さないため、クエリーは明らかに破壊的です。

今、これらの例ではメソッドが悪くならないと言われています。 Guava library(誰もが使用するはずです)には、Iteratorsクラスが提供されています。詳細については、size()というコレクションフレームワークに準拠しています。このような方法のユーザーは、どのような種類のデータを処理しているのか知っていて、交換できないことがわかっているIteratorの結果の数を数えようとするなど、不注意な呼び出しを行わないようにすることが重要です。

+1

+1の例を見つけるために! – assylias

2

いいえ、コレクションを反復処理してもコレクションは変更されません。 Iteratorクラスにはremove()メソッドがあります。これは、反復処理中にコレクションから要素を削除する唯一の安全な方法です。しかし、単にhasNext()next()と呼んでもコレクションは変更されません。

next()によって返されたオブジェクトを変更すると、それらの変更がコレクションに存在することに注意してください。

+1

これは合理的な動作ですが、反復されるときに「変更される」コレクションは、依然として反復規約(明らかに)に準拠します。これは、その質問に関するものです。 – assylias

+0

コレクション(Collections Frameworkの何か)を反復処理してもコレクションは変更されませんが、任意に定義された 'Iterator'や' Iterable'の中には、選択した場合にそうすることはできません。乱数を返す 'Iterator'を考えてみましょう。一度 'next()'が呼び出されると、その番号は事実上 "なくなります"。 – dimo414

0

考えてみてください。物事を返すメソッドは(正しく書かれていれば)アクセサーメソッドであり、データを返すだけです。彼らはそれを変更しません(mutatorメソッドではありません)。

ここでは、ディスク上にイテレータを実装する方法の例を示します。ご覧のとおり、実際に値は変更されません。

public class ArraySetIterator implements Iterator 
{ 
    private int nextIndex; 
    private ArraySet theArraySet; 

    public ArraySetIterator (ArraySet a) 
    { 
     this.nextIndex = 0; 
     this.theArraySet = a; 
    } 

    public boolean hasNext() 
    { 
     return this.nextIndex < this.theArraySet.size(); 
    } 

    public Object next() 
    { 
     return this.theArraySet.get(this.nextIndex++); 
    } 
} 
+1

はい*一般的に '' Iterator'は変更されません。 OPは例外的な事例を求めている。そのデータを変更しない例の 'Iterator'は無関係です。戻り値を持つメソッドは状態を決して変更しないというストーン・ルールは存在しません。 – dimo414

4

私の知る限り、Collection仕様は明示的に、コレクションを反復処理すると、それを修正しないと述べているが、標準ライブラリにはクラスが行動が(実際には少なくとも一つが、ないことを示していません。 dimo414's answerを参照してください)、そうしたクラスはすべて疑わしいでしょう。私はあなたがこれについて心配する必要はないと思う。

Guavaライブラリは、あなたと同じ方法でIterators.size()Iterables.size()を実装しているので、一般的なケースでは安全です。

+1

+1。 'java.util.Iterator'がそのようなものを指定したとしても、実際にはバインディング要件ではないことに注意してください。クラスが実際にインターフェイスの契約に準拠していないことはかなり頻繁に起こります。 (例えば、JDKの 'java.util.IdentityHashMap'は完全に、意図的に、' java.util.Map'の一般規約に違反しています) – ruakh

+1

'Collection'の子以外のオブジェクトは' Iterator's 。 – dimo414

関連する問題