他の人が言ったように、 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 を使用しています。
そのため、さまざまな注意点がありますが、多くの場合、これは依然として有用なイディオムです。