JavaのIteratorがIterableではないのはなぜですか?
質問
Iterator
インターフェースがIterable
を拡張しないのはなぜですか?
iterator()
メソッドは、単にthis
を返すことができます。
それは意図的なものですか、それとも単にJavaのデザイナーの監視ですか?
次のような反復子でfor-eachループを使用できると便利です。
for(Object o : someContainer.listSomeObjects()) {
....
}
where listSomeObjects()
はイテレータを返します。
解決
反復子は通常、コレクション内の単一のインスタンスを指すためです。 Iterableは、オブジェクトからイテレータを取得してその要素をトラバースできることを意味します。イテレータが表すものである単一のインスタンスを反復する必要はありません。
他のヒント
イテレータはステートフルです。アイデアは、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番目のループは常に即座に終了します。
0.02ドルの場合、IteratorはIterableを実装しないことに完全に同意しますが、拡張forループはどちらでも受け入れられると思います。 <!> quot;イテレータを反復可能にする<!> quot;引数は言語の欠陥の回避策として浮上します。
拡張forループが導入された全体の理由は、<!> quot;コレクションと配列を反復処理する際に、イテレータとインデックス変数の退屈でエラーが発生しやすい<!> quot; [ 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項目ずつ前進するだけです。 / p>
また、これを許可すると、列挙のforループを簡単に使用できるようになります。これは、他で指摘したように、イテラブルではなくイテレータに類似しています。
したがって、イテレータにIterableを実装させないで、forループを更新していずれかを受け入れます。
乾杯、
他の人が指摘したように、 Iterator
および< a href = "http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html"> Iterable
は2つの異なるものです。
また、<=>実装は、拡張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();
}
Java 8では、<=>を<=>に適応させるのが簡単になります:
for (String s : (Iterable<String>) () -> iterator) {
他の人が言ったように、Iterableは複数回呼び出すことができ、呼び出しごとに新しいIteratorを返します。イテレータは一度だけ使用されます。したがって、それらは関連していますが、異なる目的に役立ちます。しかし、いらいらすることに、<!> quot; compact for <!> quot;メソッドは反復可能オブジェクトでのみ機能します。
以下で説明するのは、両方の長所を活用するための1つの方法です。データの基になるシーケンスが1回限りの場合でもIterableを返します(より良い構文のため)。
トリックは、実際に作業をトリガーするIterableの匿名実装を返すことです。そのため、1回限りのシーケンスを生成する作業を行ってからその反復子を返す代わりに、アクセスされるたびに作業をやり直すIterableを返します。それは無駄に思えるかもしれませんが、多くの場合、とにかく一度だけIterableを呼び出すだけで、それを複数回呼び出しても、それはまだ合理的なセマンティクスを持っています(イテレーター<!> quot; looklike <!> quot; 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ループを使用できます。
このアプローチは<!> quot; lazy <!> quot;です。 -Iterableが要求されたときに作業は行われませんが、後でコンテンツが反復処理されたときにのみ作業が行われます。その結果を認識する必要があります。 DAOを使用した例では、データベーストランザクション内の結果を反復処理します。
したがって、さまざまな注意事項がありますが、これは多くの場合、依然として便利なイディオムになります。
信じられないことに、誰もまだこの答えを出していない。 <!> quot;簡単に<!> quot;新しいJava 8 Iterator.forEachRemaining()
メソッド:
Iterator<String> it = ...
it.forEachRemaining(System.out::println);
もちろん、<!> quot; simpler <!> quot;もあります。 foreachループを直接使用して、Iterable
を<=>ラムダでラップするソリューション:
for (String s : (Iterable<String>) () -> it)
System.out.println(s);
Iterator
は、何かを繰り返すことができるインターフェースです。ある種のコレクションを移動する実装です。
Iterable
は、アクセス可能なイテレーターが含まれていることを示す機能的なインターフェースです。
Java8では、これにより作業が非常に簡単になります... <=>があり、<=>が必要な場合は、次のようにします。
Iterator<T> someIterator;
Iterable<T> = ()->someIterator;
これはforループでも機能します:
for (T item : ()->someIterator){
//doSomething with item
}
これを行う多くの人もいます:
public Iterator iterator() {
return this;
}
しかし、それは正しくありません! この方法はあなたが望むものではありません!
メソッドiterator()
は、最初から新しいイテレータを返すことになっています。
そのため、次のようにする必要があります。
public class IterableIterator implements Iterator, Iterable {
//Constructor
IterableIterator(IterableIterator iter)
{
this.initdata = iter.initdata;
}
// methods of Iterable
public Iterator iterator() {
return new MyClass(this.somedata);
}
// methods of Iterator
public boolean hasNext() {
// ...
}
public Object next() {
// ...
}
public void remove() {
// ...
}
}
問題は、これを実行する抽象クラスを作成する方法はありますか? IterableIteratorを取得するには、2つのメソッドnext()およびhasNext()
を実装するだけです回避策を求めてここに来た場合は、 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
受け入れられた答えに同意しますが、自分の説明を追加したいと思います。
-
イテレータは移動の状態を表します。たとえば、イテレータから現在の要素を取得して、次の要素に進むことができます。
-
Iterableはトラバース可能なコレクションを表し、必要な数のイテレータを返すことができます。各イテレータはトラバースの独自の状態を表します。1つのイテレータは最初の要素を指し、もう1つのイテレータは3番目の要素。
Java forループがIteratorとIterableの両方を受け入れると便利です。
簡単にするために、IteratorとIterableは2つの異なる概念です。Iterableは単に<!> quot; Iを返すことができますIterator <!> quot;です。コードは次のようにすべきだと思います:
for(Object o : someContainer) {
}
someContainer instanceof SomeContainer extends Iterable<Object>
余談ですが、ScalaのIteratorにはtoIterable()メソッドがあります。 scalaの暗黙的または明示的な反復子から反復可能への変換を参照してください。
関連するメモでは、Apache Commons Collections4のIteratorIterableアダプターが役立つことがあります。反復子からインスタンスを作成するだけで、対応する反復可能オブジェクトが得られます。
ID:org.apache.commons:commons-collections4:4.0
イテレータはステートフルで、<!> quot; next <!> quot;があります。要素と<!> quot; exhausted <!> quot;一度繰り返しました。問題の場所を確認するには、次のコードを実行します。印刷される数字の数は?
Iterator<Integer> iterator = Arrays.asList(1,2,3).iterator();
Iterable<Integer> myIterable = ()->iterator;
for(Integer i : myIterable) System.out.print(i);
System.out.println();
for(Integer i : myIterable) System.out.print(i);
次の例を試すことができます:
List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
System.out.println(iterator.next());
}