184

Iteratorインターフェイスが拡張されないのはなぜIterableですか?

iterator()メソッドは単純に を返すことができますthis

それは意図的なものですか、それとも Java の設計者の単なる見落としですか?

次のような反復子で for-each ループを使用できると便利です。

for(Object o : someContainer.listSomeObjects()) {
    ....
}

wherelistSomeObjects()は反復子を返します。

4

16 に答える 16

226

イテレータはステートフルです。アイデアは、Iterable.iterator()2回呼び出すと、独立したイテレータを取得するということです-とにかく、ほとんどのイテラブル。それは明らかにあなたのシナリオには当てはまりません。

たとえば、通常は次のように書くことができます。

public void iterateOver(Iterable<String> strings)
{
    for (String x : strings)
    {
         System.out.println(x);
    }
    for (String x : strings)
    {
         System.out.println(x);
    }
}

コレクションを2回出力する必要がありますが、スキームでは2番目のループは常に即座に終了します。

于 2009-05-08T10:23:35.873 に答える
68

通常、反復子はコレクション内の単一のインスタンスを指すためです。Iterable は、オブジェクトからイテレータを取得してその要素をトラバースできることを意味します。また、イテレータが表す単一のインスタンスを反復する必要はありません。

于 2009-05-08T10:23:01.943 に答える
65

私の $0.02 については、Iterator が Iterable を実装すべきではないことに完全に同意しますが、強化された for ループはどちらも受け入れるべきだと思います。「イテレータを反復可能にする」という議論全体が、言語の欠陥に対する回避策として出てくると思います。

強化された for ループが導入された全体的な理由は、「コレクションと配列を反復処理する際のイテレータとインデックス変数の煩わしさとエラーの発生しやすさを排除する」ためでした [ 1 ]。

Collection<Item> items...

for (Iterator<Item> iter = items.iterator(); iter.hasNext(); ) {
    Item item = iter.next();
    ...
}

for (Item item : items) {
    ...
}

では、なぜこれと同じ議論が反復子には当てはまらないのでしょうか?

Iterator<Iter> iter...
..
while (iter.hasNext()) {
    Item item = iter.next();
    ...
}

for (Item item : iter) {
    ...
}

どちらの場合も、hasNext() と next() の呼び出しが削除されており、内側のループで反復子への参照がありません。はい、Iterables を再利用して複数のイテレータを作成できることは理解していますが、それはすべて for ループの外で行われます。ループ内では、イテレータによって返された項目に対して、一度に 1 つの項目だけが前進します。

また、これを許可すると、他の場所で指摘されているように、Iterables ではなく Iterators に類似した Enumerations の for ループを使用することも容易になります。

したがって、Iterator に Iterable を実装させないでください。ただし、for ループを更新してどちらかを受け入れるようにしてください。

乾杯、

于 2011-03-10T23:18:48.053 に答える
18

他の人が指摘したように、IteratorIterableは2つの異なるものです。

また、Iterator実装は強化された for ループよりも前から存在します。

静的メソッドのインポートで使用すると、次のような単純なアダプター メソッドを使用して、この制限を克服することも簡単です。

for (String line : in(lines)) {
  System.out.println(line);
}

実装例:

  /**
   * Adapts an {@link Iterator} to an {@link Iterable} for use in enhanced for
   * loops. If {@link Iterable#iterator()} is invoked more than once, an
   * {@link IllegalStateException} is thrown.
   */
  public static <T> Iterable<T> in(final Iterator<T> iterator) {
    assert iterator != null;
    class SingleUseIterable implements Iterable<T> {
      private boolean used = false;

      @Override
      public Iterator<T> iterator() {
        if (used) {
          throw new IllegalStateException("SingleUseIterable already invoked");
        }
        used = true;
        return iterator;
      }
    }
    return new SingleUseIterable();
  }

IteratorJava 8 では、 anを anに適応させることIterableがより簡単になります。

for (String s : (Iterable<String>) () -> iterator) {
于 2011-12-18T21:59:23.607 に答える
9

信じられないことに、まだ誰もこの答えを出していません。Iterator新しい Java 8Iterator.forEachRemaining()メソッドを使用して を「簡単に」反復する方法は次のとおりです。

Iterator<String> it = ...
it.forEachRemaining(System.out::println);

もちろん、 foreach ループをラムダでラップIteratorして直接操作する「より単純な」ソリューションがあります。Iterable

for (String s : (Iterable<String>) () -> it)
    System.out.println(s);
于 2016-12-21T06:35:55.230 に答える
8

他の人が言ったように、 Iterable は複数回呼び出すことができ、呼び出しごとに新しい Iterator を返します。Iterator は一度だけ使用されます。したがって、それらは関連していますが、異なる目的を果たします。しかし、イライラすることに、"compact for" メソッドは iterable でしか機能しません。

以下で説明するのは、両方の長所を活用する 1 つの方法です。基になるデータ シーケンスが 1 回限りの場合でも、Iterable を返します (構文を改善するため)。

秘訣は、実際に作業をトリガーする Iterable の匿名実装を返すことです。したがって、1 回限りのシーケンスを生成する作業を行ってから Iterator を返す代わりに、アクセスされるたびに作業をやり直す Iterable を返します。それは無駄に思えるかもしれませんが、多くの場合、とにかく Iterable を 1 回だけ呼び出します。また、複数回呼び出したとしても、妥当なセマンティクスがあります (Iterator を Iterable に「似せる」単純なラッパーとは異なり、これはそうではありません。 2 回使用すると失敗します)。

たとえば、データベースから一連のオブジェクトを提供する DAO があり、イテレータを介してアクセスできるようにしたいとします (たとえば、不要な場合にすべてのオブジェクトをメモリ内に作成しないようにするため)。これで、イテレータを返すことができましたが、ループ内で返された値を使用するのは見苦しくなります。代わりに、すべてを anon Iterable でラップします。

class MetricDao {
    ...
    /**
     * @return All known metrics.
     */
    public final Iterable<Metric> loadAll() {
        return new Iterable<Metric>() {
            @Override
            public Iterator<Metric> iterator() {
                return sessionFactory.getCurrentSession()
                        .createQuery("from Metric as metric")
                        .iterate();
            }
        };
    }
}

これは、次のようなコードで使用できます。

class DaoUser {
    private MetricDao dao;
    for (Metric existing : dao.loadAll()) {
        // do stuff here...
    }
}

これにより、インクリメンタル メモリの使用を維持しながら、コンパクトな for ループを使用できます。

このアプローチは「怠惰」です。作業は Iterable が要求されたときに行われるのではなく、後でコンテンツが反復処理されたときに行われます。その結果を認識する必要があります。この例では、データベース トランザクション内で結果を反復処理することを意味する DAO を使用しています。

そのため、さまざまな注意点がありますが、多くの場合、これは依然として有用なイディオムです。

于 2012-02-06T15:49:26.243 に答える
2

また、多くの人がこれを行っているのを見ます:

public Iterator iterator() {
    return this;
}

しかし、それは正しくありません!この方法はあなたが望むものではありません!

このメソッドiterator()は、最初から新しい反復子を返すことになっています。したがって、次のようなことをする必要があります。

public class IterableIterator implements Iterator, Iterable {

  //Constructor
  IterableIterator(SomeType initdata)
  {
    this.initdata = iter.initdata;
  }
  // methods of Iterable

  public Iterator iterator() {
    return new IterableIterator(this.intidata);
  }

  // methods of Iterator

  public boolean hasNext() {
    // ...
  }

  public Object next() {
    // ...
  }

  public void remove() {
    // ...
  }
}

問題は、これを実行する抽象クラスを作成する方法はありますか? そのため、IterableIterator を取得するには、next() と hasNext() の 2 つのメソッドを実装するだけで済みます。

于 2010-10-03T10:54:07.973 に答える
1

回避策を探してここに来た場合は、IteratorIterableを使用できます。(Java 1.6 以降で使用可能)

使用例 (ベクトルを逆にする)。

import java.util.Vector;
import org.apache.commons.collections4.iterators.IteratorIterable;
import org.apache.commons.collections4.iterators.ReverseListIterator;
public class Test {
    public static void main(String ... args) {
        Vector<String> vs = new Vector<String>();
        vs.add("one");
        vs.add("two");
        for ( String s: vs ) {
            System.out.println(s);
        }
        Iterable<String> is
            = new IteratorIterable(new ReverseListIterator(vs));
        for ( String s: is ) {
            System.out.println(s);
        }
    }
}

版画

one
two
two
one
于 2016-03-07T19:25:35.180 に答える
0

余談ですが、ScalaにはIteratorにtoIterable()メソッドがあります。イテレータからイテレータへのscalaの暗黙的または明示的な変換を参照してください

于 2010-11-09T10:12:19.190 に答える
0

簡単にするために、Iterator と Iterable は 2 つの異なる概念です。Iterable は単に「I can return an Iterator」の略です。あなたのコードは次のようになるべきだと思います:

for(Object o : someContainer) {
}

with someContainer instanceofSomeContainer extends Iterable<Object>

于 2009-05-08T10:37:54.780 に答える
0

これに関連して、Apache Commons Collections4 の IteratorIterable アダプターが役立つ場合があります。イテレータからインスタンスを作成するだけで、対応するイテラブルが得られます。

https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html

ID: org.apache.commons:commons-collections4:4.0

于 2015-03-16T16:00:33.537 に答える
-1

次の例を試すことができます。

List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
    System.out.println(iterator.next());
}
于 2013-06-19T10:10:59.250 に答える