1. ホーム
  2. java

[解決済み] なぜJavaはイテレータに対するforeachを許さないのか(イテレータブルに対してのみ)?重複

2023-06-02 07:23:05

質問

重複の可能性があります。

なぜJavaのIteratorはIterableではないのですか?

イテレータを与えてfor-eachループを使用する慣用的な方法?

Iterator型のオブジェクトを反復するためにfor-eachループを使用することは可能ですか?

foreachループは、私の知る限りJava5で追加されたシンタックスシュガーです。そのため

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

と同じバイトコードを生成します。

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

しかし、そもそも反復子がなく、イテレータしかない場合(例えば、クラスが2つの異なるイテレータを提供しているため)、構文糖のforeachループは使えません。もちろん、古いスタイルの反復処理を行うことはできます。しかし、私は実際にやってみたいのです。

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;
    }
}

(実際には、一度しか反復できないので、Iterable API の醜い乱用です!)

もしそれが Iterator を中心に設計されていれば、多くの面白いことができるかもしれません。

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.

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

あるいは、私が知らないだけで、何か言語のトリックがあるのでしょうか?

どのように解決するのですか?

というわけで、なんとなく納得のいく説明ができました。

短いバージョンです。 この構文は 配列 にも適用されるからです。

もし構文が Iterator を中心に構文を設計した場合、配列と矛盾してしまいます。3つのバリエーションを挙げてみましょう。

A) をJavaの開発者が選択しました。

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つとも許可します。

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

さて、もし誰かが 両方 IterableIterator (forループは新しいイテレータを取得するのか、それとも現在のイテレータを反復するのか - ツリー状の構造では簡単に発生します!?). Iterable beats Iterator" のような単純なタイブレーカーでは、残念ながらうまくいきません:それは突然、実行時対コンパイル時の違いとジェネリックの問題を導入してしまいます。

今、突然、私たちはコレクション/イテレータブルまたは配列に対して反復処理したいかどうかに注意を払う必要があり、その時点で私たちは大きな混乱の代償としてほとんど利益を得ていません。

Javaにおける"for each"の方法(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) { }

一度だけデータを繰り返し処理します。