26

私が作った次のクラスを見る:

public class FibonacciSupplier implements Iterator<Integer> {
    private final IntPredicate hasNextPredicate;

    private int beforePrevious = 0;
    private int previous = 1;

    private FibonacciSupplier(final IntPredicate hasNextPredicate) {
        this.hasNextPredicate = hasNextPredicate;
    }

    @Override
    public boolean hasNext() {
        return hasNextPredicate.test(previous);
    }

    @Override
    public Integer next() {
        int result = beforePrevious + previous;
        beforePrevious = previous;
        previous = result;
        return result;
    }

    public static FibonacciSupplier infinite() {
        return new FibonacciSupplier(i -> true);
    }

    public static FibonacciSupplier finite(final IntPredicate predicate) {
        return new FibonacciSupplier(predicate);
    }
} 

そして、それを次のように使用します。

public class Problem2 extends Problem<Integer> {
    @Override
    public void run() {
        result = toList(FibonacciSupplier.finite(i -> (i <= 4_000_000)))
                .stream()
                .filter(i -> (i % 2 == 0))
                .mapToInt(i -> i)
                .sum();
    }

    @Override
    public String getName() {
        return "Problem 2";
    }

    private static <E> List<E> toList(final Iterator<E> iterator) {
        List<E> list = new ArrayList<>();
        while (iterator.hasNext()) {
            list.add(iterator.next());
        }
        return list;
    }
}

どうすれば無限 を作成できますStream<E>か?

を使用するStream<Integer> infiniteStream = toList(FibonacciSupplier.infinite()).stream()と、おそらく驚くべきことに、無限ストリームを取得することはありません。
代わりに、コードはlist基になるメソッドでの作成で永遠にループします。

これまでのところ、これは純粋に理論的なものですが、最初に無限ストリームから最初の x 個の数字をスキップしてから、最後の y 個の数字で制限したい場合、その必要性を明確に理解できます。たとえば、次のようになります。

int x = MAGIC_NUMBER_X;
int y = MAGIC_NUMBER_y;
int sum = toList(FibonacciSupplier.infinite())
    .stream()
    .skip(x)
    .limit(y)
    .mapToInt(i -> i)
    .sum();

コードが結果を返すことはありません。どうすればよいですか?

4

4 に答える 4

27

を作成するにはIteratorまたはが必要だと考えるのは間違いです。無限ストリームを作成するには、次々と値を提供する単一のメソッドで十分です。したがって、クラスの最も簡単な使用法は次のとおりです。CollectionStreamFibonacciSupplier

IntStream s=IntStream.generate(FibonacciSupplier.infinite()::next);

または、ボックス化された値を好む場合:

Stream<Integer> s=Stream.generate(FibonacciSupplier.infinite()::next);

この場合、メソッドに名前を付ける必要も、インターフェースnextを満たす必要もないことに注意してください。Iteratorしかし、それがあなたのクラスと同じかどうかは問題ではありません。nextさらに、メソッドを として使用するようにストリームに指示したSupplierので、hasNextメソッドが呼び出されることはありません。それはまさに無限です。

your を使用して有限ストリームを作成するのIteratorは、もう少し複雑です。

Stream<Integer> s=StreamSupport.stream(
  Spliterators.spliteratorUnknownSize(
    FibonacciSupplier.finite(intPredicate), Spliterator.ORDERED),
  false);

この場合、ボックス化されていない値を持つ有限が必要な場合IntStreamintFibonacciSupplier実装する必要がありますPrimitiveIterator.OfInt

于 2014-02-24T10:54:56.463 に答える
16

Java 8 には、インターフェイスStreamを実装するパブリックで具象的なクラスはありません。ただし、静的ファクトリ メソッドがいくつかあります。最も重要なものの 1 つはStreamSupport.streamです。特に、ほとんどのコレクション クラスに継承されるデフォルト メソッドCollection.streamで使用されます。

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

このメソッドのデフォルトの実装では、を呼び出してSpliteratorspliterator()を作成し、作成したオブジェクトをファクトリ メソッドに渡します。Spliteratorは、並列ストリームをサポートするために Java 8 で導入された新しいインターフェースです。これはIteratorに似ていますが、後者とは対照的に、Spliteratorは独立して処理できる部分に分割できます。詳細については、 Spliterator.trySplitを参照してください。

デフォルト のメソッドIterable.spliteratorも Java 8 で追加されたため、すべてのIterableクラスが自動的に Spliterators をサポートします。実装は次のようになります。

default Spliterator<T> spliterator() {
    return Spliterators.spliteratorUnknownSize(iterator(), 0);
}

このメソッドは、任意のIteratorからSpliteratorを作成します。これら 2 つの手順を組み合わせると、任意のIteratorからStreamを作成できます。

<T> Stream<T> stream(Iterator<T> iterator) {
    Spliterator<T> spliterator
        = Spliterators.spliteratorUnknownSize(iterator, 0);
    return StreamSupport.stream(spliterator, false);
}

Spliteratorsの印象をつかむために、コレクションを使用しない非常に単純な例を次に示します。次のクラスは、 Spliteratorを実装して、整数の半開区間を反復処理します。

public final class IntRange implements Spliterator.OfInt {
    private int first, last;
    public IntRange(int first, int last) {
        this.first = first;
        this.last = last;
    }
    public boolean tryAdvance(IntConsumer action) {
        if (first < last) {
            action.accept(first++);
            return true;
        } else {
            return false;
        }
    }
    public OfInt trySplit() {
        int size = last - first;
        if (size >= 10) {
            int temp = first;
            first += size / 2;
            return new IntRange(temp, first);
        } else {
            return null;
        }
    }
    public long estimateSize() {
        return Math.max(last - first, 0);
    }
    public int characteristics() {
        return ORDERED | DISTINCT | SIZED | NONNULL
            | IMMUTABLE | CONCURRENT | SUBSIZED;
    }
}
于 2014-02-22T16:19:31.327 に答える
0

低レベルのストリーム サポート プリミティブとSpliteratorsライブラリを使用して、 からストリームを作成できますIterator

最後のパラメータStreamSupport.stream()は、ストリームが並列ではないことを示しています。フィボナッチ反復子は以前の反復に依存するため、必ずそのようにしてください。

return StreamSupport.stream( Spliterators.spliteratorUnknownSize( new Iterator<Node>()
{
    @Override
    public boolean hasNext()
    {
        // to implement
        return ...;
    }

    @Override
    public ContentVersion next()
    {
        // to implement
        return ...;
    }
}, Spliterator.ORDERED ), false );
于 2017-05-18T20:20:32.223 に答える