1

イテレータが NoSuchElementException を発生させてループの実行を停止するときに foreach ループが考慮されないのはなぜですか?

説明させてください。

通常、反復子は hasNext と next という 2 つの重要なメソッドを提供する必要があります。反復する要素がこれ以上ない場合、次のメソッドは NoSuchElementException を発生させる可能性があります。hasNext メソッドは、コードを少しだけ改善するための構文シュガーのようなものです。重要なメソッドは実際には next であり、これは何らかの作業を行うべきメソッドですが、hasNext ではなく、hasNext が事前に呼び出されているという仮定なしで実装する必要があります。

次のような状況があるとします。

final Iterator<String> bogusIt01 = new Iterator<String>() {

    public boolean hasNext() {
        return true;
    }

    public String next() {
        throw new NoSuchElementException("no such element");
    }

    public void remove() {
        throw new UnsupportedOperationException("cannot remove element");
    }

};

final Iterable<String> bogusPr01 = new Iterable<String>() {

    public Iterator<String> iterator() {
        return bogusIt01;
    }

};

for (@SuppressWarnings("unused") String string:bogusPr01) {
    // pass
}

これにより、NoSuchElementException が発生します。ループが終了することを期待しています。私はそれを間違っていますか?

私が質問している理由は、このモデルを使用すると、別の反復子をラップしてその出力に対して何らかの処理を行う反復子を作成することが非常に困難になるためです。次のシナリオを想像してみましょう。

Iterator1 <- これはオリジナルのイテレータで、問題ありません。

Iterator2 <- この反復子は Iterator1 をラップし、反復された要素の出力を変更します。

Iterator2 は次のように実装されます。

    public boolean hasNext() {
        return iterator1.hasNext();
    }

    public Object next() {
        if (!iterator1.hasNext()) {
            throw new NoSuchElementException("no such element");
        }

        Object nextValue = iterator1.next();

        try {
            // do something that could raise exception
        } catch (Exception e) {
            return this.next();
        }

        return productValue;
    }

    public void remove() {
        throw new UnsupportedOperationException("cannot remove element");
    }

そのような設計は、私には非常に理にかなっていますが、まったく不可能です.

これを行うより良い方法はありますか?

4

3 に答える 3

1

このコードの問題は、前の への呼び出しがtrue を返した場合、 がfrom をIteratorスローしてはならないことです。次のようなことを検討するかもしれません。NoSuchElementExceptiongetNexthasNext

  1. inhasNextは、基になるイテレータから次の適切な要素を取得し、次の への呼び出しまでキャッシュしnextます。nextキャッシュされた値を返すだけです。キャッシュされた値が null の場合hasNextは呼び出されず、適切に NSEE をスローできます。

考慮したい他のオプションは、Guava のIterables.transformorを使用することですLists.transform。これは、 を取得し、Iterable各要素に対して何らかの変換を実行するように設計されています。null変換できなかった値を返し、Iterables.filterを使用してPredicates.notNull Predicatenull ではない要素を削除することができます。

Iterables.transform

Iterables.filter

Predicates.notNull

Guava を使用すると、上記のコードは次のようになります。

Function<A, B> myFunction = new Function<A,B>(){
   public B apply(A input){
      try{ // do work
         return new B();
      catch(Exception e){ return null; }          
   }
}

Iterable<A> inputList = ...;
Iterable<B> newList = Iterables.filter(
       Iterables.transform(inputList, myFunction), 
       Predicates.notNull());

for (B b : newList)...
于 2012-10-18T10:42:41.893 に答える
0

以下はいかがでしょうか?

import java.util.Iterator;

public class BreakingIterator<T> implements Iterator<T> {

    private Iterator<T> iterator;
    private T nextValue;
    private RuntimeException nextException;

    /* -------------------------------------------------------------------- */

    public BreakingIterator(Iterator<T> iterator) {
        this.iterator = iterator;
        this.nextValue = null;
        this.nextException = null;
    }

    /* -------------------------------------------------------------------- */

    public boolean hasNext() {
        if (this.iterator.hasNext()) {
            try {
                this.nextValue = this.iterator.next();
            } catch (RuntimeException e) {
                this.nextValue = null;
                this.nextException = e;

                return false;
            }

            return true;
        } else {
            return false;
        }
    }

    public T next() {
        if (this.nextException != null) {
            throw this.nextException;
        } else {
            return this.nextValue;
        }
    }

    public void remove() {
        this.iterator.remove();
    }

}

この実装により、すべてのハードワークが next から hasNext に移されます。

于 2012-10-18T11:04:15.150 に答える
0

Iterator.hasNext()の Javadoc から

反復にさらに要素がある場合は true を返します。(つまり、 next() が例外をスローするのではなく、要素を返す場合は true を返します。)

一般に、指定された API に準拠していない場合、JVM は何をすべきかを推測したり、回避策を実行したりしません。代わりに、取得した例外またはエラーが生成されます。

例外を引き起こす可能性のあるイテレータをラップしている場合は、その例外をスローするか、発生しなかったふりをした方がよいと思われる場合 (私見ではありそうにありません)、next() を呼び出す前にこれを確認する必要があります。

Object productValue = null;
public boolean hasNext() {
    produceValue = null;
    while (iterator1.hasNext()) {
        Object nextValue = iterator1.next();

        try {
            // do something that could raise exception
            produceValue = ....
            return true;
        } catch (Exception e) {
            // handle the exception.
        }
    }
    return false;
}

public Object next() {
    if (produceValue == null && !hasNext())
        throw new NoSuchElementException("no such element");
    Object ret = productValue;
    productValue = null;
    return ret;
}

public void remove() {
    throw new UnsupportedOperationException("cannot remove element");
}
于 2012-10-18T10:42:17.637 に答える