2012-06-26 9 views
53

可能性の重複:私はそうJavaがイテレータでforeachを許可しないのはなぜですか(イテラブルのみ)。

のJava 5で追加したシンタックスシュガーを知っているよう
Why is Java's Iterator not an Iterable?

Idiomatic way to use for-each loop given an iterator?

Can we use for-each loop for iterating the objects of Iterator type?

foreachループは限りあります

Iterable<O> iterable; 
for(O o : iterable) { 
    // Do something 
} 

(クラスが2つの異なるイテレータを提供していますので、例えば)私は最初の場所で反復可能な、唯一のイテレータを持っていない場合、基本的に、しかし

Iterable<O> iterable; 
for(Iterator<O> iter = iterable.iterator(); iter.hasNext(); /* NOOP */) { 
    O o = iter.next(); 
    // Do something 
} 

と同じバイトコードを生成します、私はできませんsugar foreach loopという構文を使用します。当然のことながら、私はまだ古風なスタイルの繰り返しを行うことができます。しかし、私は実際にやってみたい:

Iterator<O> iter; 
for(O o : iter /* Iterator<O>, not Iterable<O>! */) { 
    // Do something 
} 

をそしてもちろん、私は行うことができますIterable偽:

class Adapter<O> implements Iterable<O> { 
    Iterator<O> iter; 

    public Adapter(Iterator<O> iter) { 
     this.iter = iter; 
    } 

    @Override 
    public Iterator<O> iterator() { 
     return iter; 
    } 
} 

(実際には反復処理可能なAPIの醜い乱用されており、それができるように!

for(O o : iterable.iterator()) {} // Iterate over Iterable and Collections 

for(O o : list.backwardsIterator()) {} // Or backwards 

Iterator<O> iter; 
for(O o : iter) { 
    if (o.something()) { iter.remove(); } 
    if (o.something()) { break; } 
} 
for(O : iter) { } // Do something with the remaining elements only. 
:一度だけ繰り返すこと)

それは代わりに、反復可能なのIteratorを中心に設計された場合は、1が面白いの数を行うことができます

誰でも知っていますかなぜ言語はこのように設計されていますか?クラスがIteratorIterableの両方を実装する場合のあいまいさを避けるには? "for(O o:iter)"がすべての要素を2回処理することを想定したプログラマエラーを避けるために(そして、新しいイテレータを忘れる)?それとも何か他の理由がありますか?

または、わからない言語のトリックはありますか?

+1

[この1つ](http://stackoverflow.com/questions/2162917/can-we-use-for-each-loop-for-iterating-the-objects-of-iterator-type) –

+1

どちらも重複していますが、完全に同じではありません。 IteratorはIterableであってはなりません。なぜなら、一度しか反復することができないからです( "再起動"しない)。 そして、あなたがイテレータでforeachを使用することはできないことを知っています(私が概説したアダプタを使用しない限り)が、なぜJavaがこのように設計されたのか理解しようとしています。 –

+0

Meh;私は反対する傾向がある。 1.5の文書で与えられた答えは、デザイナーが最も一般的なユースケースを扱うためにこの方法を選んだということだけを述べています。時には答えは*です。 –

答えて

17

だから私は今、やや合理的な説明があります。

ショートバージョン:を構文はまた、イテレータを持っていない配列、に適用されるので。

私が提案したように構文がIteratorの周りに設計されていると、配列と矛盾します。 Java開発者が選択するよう

A):私は3つの変形を与えてみましょう

Object[] array; 
for(Object o : array) { } 
Iterable<Object> list; 
for(Object o : list) { } 
Iterator<Object> iter; 
while(iter.hasNext()) { Object o = iter.next(); } 

ザ・は同じように動作し、配列やコレクション間非常に一貫です。 しかし、反復子は古典的な反復スタイル(少なくともエラーを引き起こす可能性は低い)を使用する必要があります。

B)が可能アレイとIterators

Object[] array; 
for(Object o : array) { } 
Iterable<Object> list; 
for(Object o : list.iterator()) { } 
Iterator<Object> iter; 
for(Object o : iter) { } 

今アレイとコレクションは矛盾しています。配列とArrayListは非常に密接に関連しており、同じように動作するはずです。どの時点であれば、言語はであり、例えばになります。配列にIterableが実装されていると、一貫性が失われます。

C)は、3つのすべてを許可する:

いずれかの誰かが 両方 IterableIteratorを実装したときに、我々は不明瞭な状況で終わる場合は今(ループ用の新しいイテレータまたは反復を取得することになっている
Object[] array; 
for(Object o : array) { } 
Iterable<Object> list; 
for(Object o : list) { } 
Iterator<Object> iter; 
for(Object o : iter) { } 

現在と比べると木のような構造で簡単に起こる!シンプルなタイブレイカーのAla「反復可能なビートイテレータ」は、残念ながら実行しません。突然実行時とコンパイル時の違いとジェネリックの問題が発生します。

突然、私たちはコレクション/イテラブルまたは配列を繰り返し処理するかどうかに注意を払う必要があります。その時点で大きな混乱を犠牲にしてほとんど利益を得られませんでした。

"for each"がJava(A)にある方法は非常に一貫しており、プログラミングエラーはほとんど発生せず、将来の可能性のある配列を通常のオブジェクトに変更できる可能性があります。

D)もありますが、それでも大丈夫です: for-each for Iterators only。できれプリミティブ配列に.iterator()メソッドを追加することによって:

Object[] array; 
for(Object o : array.iterator()) { } 
Iterable<Object> list; 
for(Object o : list.iterator()) { } 
Iterator<Object> iter; 
for(Object o : iter) { } 

をしかし、これは、ランタイム環境の変化だけでなく、コンパイラ、および休憩後方互換性が必要です。さらに、前述の混乱は依然として存在します。

Iterator<Object> iter; 
for(Object o : iter) { } 
for(Object o : iter) { } 

データを1回だけ繰り返します。

+0

* "Iteratorの構文では、書き込む必要があります..." *なぜですか? –

+0

それ以外の場合は、クラスに 'Iterable'と' Iterator'の両方を実装させて大きな混乱を招くことになります。 –

+0

そして配列とは何が関係していますか?とにかく、配列を参照することなく、私はそれを行うことができます - そして、私は私の答え(スラッシュ "推測" :-))で呼び出す複雑さの*ちょうど*です。 –

31

誰もが知っているなぜ言語はこのように設計されていますか?

のために、それぞれが唯一でき ITERあるものの上に理にかなっている、とITER ators上で意味がありませんので。イテレータを既にお持ちの場合は、単純なループでこれを行う必要があります。

は比較:私はできITER でスタート:

// Old way 
Iterator<Thingy> it = iterable.iterator(); 
while (it.hasNext()) { 
    Thingy t = it.next(); 
    // Use `t` 
} 

// New way 
for (Thingy t : iterable) { 
    // Use `t` 
} 

私はイテレータから始めバーサス:

// Old/current way 
while (iterator.hasNext()) { 
    Thing t = iterator.next(); 
    // Use `t` 
} 

// Imagined way 
for (Thingy t : iterator) { 
    // Use `t` 
} 

ことがあり多くは第二の例ではそれではない、と特別なケースを作成することによってfor-eachの意味を複雑にします。

「なぜ」という質問は、決定に参加した主な参加者に向けられていないと常に難しいですが、追加された複雑さが限界効用に値するものではないと私は推測します。

+0

さらに、イテレータのすべてのアイテムを一度しか見ることができません。 – Qnan

+1

反復子*に反して、反復可能なものはなぜ意味がありますか?私は明らかに "このイテレータによって返されるすべての要素に対して"何かをしたいことができます。 –

+10

その引数が表示されません。 whileの代わりにforループを使って "古い方法"を書くと、かなり同じコードが得られます。そして、「古い方法」のイテレータバージョンがあなたにとってより直観的に見えたら、手を挙げてください。より良い議論は、forループが暗黙のうちに驚くかもしれない破壊的であることです - 私はまだそれを好きではありません。 – Voo

8

Iteratorインターフェイスは既に使用されていましたが、original JSRに記載されているように、その目的のために正確に作成されました(ループの拡張機能)。

+0

そのポインタをありがとう。私は今、何が理由かもしれないという大雑把な考えを持っています。私はその行についてもう少し考えなければならないでしょう、そして私は更新します。 –

+2

どうやら、 'java.util.Iterator'を使うのではなく' java.lang.Iterable'を導入する根拠は、 'java.util'のコア言語依存性を避けるためです。 ;) –

6

"for"ループはイテレータにとって破壊的であるため、 ListIteratorサブインターフェイスを実装していない限り、Iteratorはリセットできません(つまり、先頭に戻る)。

"for"ループでIteratorを使用すると、使用できなくなります。私の推測では、言語設計者はこれをコンパイラでバイトコードに変換する特別なケース(Iterableと配列用にすでに2つあります)と組み合わせて(これはイテラブルと同じ変換を再利用できませんでした)それを実装しない嫌悪者。

イテレータインターフェイスを使用してコード内でこれを行うと、少なくとも何が起こっているのかが明らかに分かります。

Iterator<String> iterator = ...; 
Collections.each (iterator, (String s) => { System.out.println(s); }); 

List<String> list = ...; 
Collections.each (list, (String s) => { System.out.println(s); }); 

後方互換性を壊し、そしてまだ比較的単純な構文を持たずに:彼らは、これはいいと簡単に作ることができてくるラムダで

。私は彼らが "each"、 "collect"、 "maps"のようなメソッドを別のインターフェースに組み込むのではないかと疑っています。これは後方互換性を破るためです。

+0

「リセット」が 'Iterable'sで' .iterator() 'を呼び出すことによって明示的に行われた場合、リセットも明白でなければなりません。まあ、おそらく1つはおそらくリセットされないことを見落とすことができます... –

+0

Iterableは繰り返し可能なループ機構を提供します。反復可能なループを複数回繰り返すと、毎回同じ結果が得られます(データ自体を掘り下げていないと仮定します)。同じことは、Iteratorをループすることができないと言えません(私が言ったように、リセットすることはできません)。 – Matt

+0

キューのようないくつかのデータ型では、 "each for"を消費するという概念は意味があります。 *繰り返し*列挙できることを約束することなく、型を "それぞれのために"できるようにすることは有益な能力のように思えますし、イテレータは本質的に一度だけ読み込むことができます。 – supercat

0

for-eachループが構文的な砂糖であるという事実に答えの一部が隠れていると思います。要点は、人々がたくさんのことをやっていることをもっと簡単にしたいということです。そして、少なくとも私の経験では、イディオム

Iterator iterator = iterable.iterator(); 
while(iterator.hasNext()) { 
    Element e = (Element)iterator.next() ; 
} 

は、すべて旧式のコードで発生しました。そして、複数のイテレーターで幻想的なことをしていませんでした。

+0

と一貫していません。技術的には、バイトコードはforループのように見えます:for(Iterator iterator = iterable.iterator(); iterator.hasNext ;) – Matt

+0

実際にJSRではそれぞれの構文について提案したところで、この種の* for *ループを使用しました。理由は変数スコープであり、Iteratorはループ内でのみ定義されます。 while構文は古典的なJavaの教科書ですが、変数スコープを正しく取得するには別の '{}'を追加する必要があります。 –

関連する問題