Wie kann ich Streams träge verketten?

Ich versuche, einen Stream zu implementieren, bei dessen Implementierung eine andere Instanz von sich selbst verwendet wird. Dem Stream sind ein paar konstante Elemente (mit IntStream.concat) vorangestellt. Dies sollte also funktionieren, solange der verkettete Stream den nicht konstanten Teil träge erstellt. Ich denke mit dem StreamSupport.intStream-Überladung nimmt einen Lieferanten mit IntStream.concat (welches "Erstellt einen verzögert verketteten Stream") sollte träge genug sein, um den zweiten Spliterator nur zu erstellen, wenn Elemente von ihm angefordert werden, aber selbst das Erstellen des Streams (ohne ihn auszuwerten) überläuft den Stapel. Wie kann ich Streams träge verketten?

Ich versuche, das Streaming-Primzahlsieb von @ zu portierediese Antwort in Java. Dieses Sieb verwendet eine andere Instanz von sich selbst ps = postponed_sieve() im Python-Code). Wenn ich die ersten vier konstanten Elemente yield 2; yield 3; yield 5; yield 7;) in ihren eigenen Stream, es ist einfach, den Generator als Spliterator zu implementieren:

/**
 * based on https://stackoverflow.com/a/10733621/3614835
 */
static class PrimeSpliterator extends Spliterators.AbstractIntSpliterator {
    private static final int CHARACTERISTICS = Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SORTED;
    private final Map<Integer, Supplier<IntStream>> sieve = new HashMap<>();
    private final PrimitiveIterator.OfInt postponedSieve = primes().iterator();
    private int p, q, c = 9;
    private Supplier<IntStream> s;
    PrimeSpliterator() {
        super(105097564 /* according to Wolfram Alpha */ - 4 /* in prefix */,
                CHARACTERISTICS);
        //p = next(ps) and next(ps) (that's Pythonic?)
        postponedSieve.nextInt();
        this.p = postponedSieve.nextInt();
        this.q = p*p;
    }

    @Override
    public boolean tryAdvance(IntConsumer action) {
        for (; c > 0 /* overflow */; c += 2) {
            Supplier<IntStream> maybeS = sieve.remove(c);
            if (maybeS != null)
                s = maybeS;
            else if (c < q) {
                action.accept(c);
                return true; //continue
            } else {
                s = () -> IntStream.iterate(q+2*p, x -> x + 2*p);
                p = postponedSieve.nextInt();
                q = p*p;
            }
            int m = s.get().filter(x -> !sieve.containsKey(x)).findFirst().getAsInt();
            sieve.put(m, s);
        }
        return false;
    }
}

Mein erster Versuch mit der primes () -Methode gibt einen IntStream zurück, der einen konstanten Stream mit einem neuen PrimeSpliterator verkettet:

public static IntStream primes() {
    return IntStream.concat(IntStream.of(2, 3, 5, 7),
            StreamSupport.intStream(new PrimeSpliterator()));
}

Calling primes () führt zu einem StackOverflowError, da primes () immer einen PrimeSpliterator instanziiert, der Feldinitialisierer von PrimeSpliterator jedoch immer primes () aufruft. Es gibt jedoch eine Überladung von StreamSupport.intStream, die einen Lieferanten benötigt, was es ermöglichen sollte, den PrimeSpliterator träge zu erstellen:

public static IntStream primes() {
    return IntStream.concat(IntStream.of(2, 3, 5, 7),
            StreamSupport.intStream(PrimeSpliterator::new, PrimeSpliterator.CHARACTERISTICS, false));
}

Ich erhalte jedoch stattdessen einen StackOverflowError mit einem anderen Backtrace (abgeschnitten, da es sich wiederholt). Beachten Sie, dass sich die Rekursion vollständig im Aufruf von primes () befindet - der Terminaloperationsiterator () wird in einem zurückgegebenen Stream niemals aufgerufen.

Exception in thread "main" java.lang.StackOverflowError
    at java.util.stream.StreamSpliterators$DelegatingSpliterator$OfInt.<init>(StreamSpliterators.java:582)
    at java.util.stream.IntPipeline.lazySpliterator(IntPipeline.java:155)
    at java.util.stream.IntPipeline$Head.lazySpliterator(IntPipeline.java:514)
    at java.util.stream.AbstractPipeline.spliterator(AbstractPipeline.java:352)
    at java.util.stream.IntPipeline.spliterator(IntPipeline.java:181)
    at java.util.stream.IntStream.concat(IntStream.java:851)
    at com.jeffreybosboom.projecteuler.util.Primes.primes(Primes.java:22)
    at com.jeffreybosboom.projecteuler.util.Primes$PrimeSpliterator.<init>(Primes.java:32)
    at com.jeffreybosboom.projecteuler.util.Primes$Lambda$1/834600351.get(Unknown Source)
    at java.util.stream.StreamSpliterators$DelegatingSpliterator.get(StreamSpliterators.java:513)
    at java.util.stream.StreamSpliterators$DelegatingSpliterator.estimateSize(StreamSpliterators.java:536)
    at java.util.stream.Streams$ConcatSpliterator.<init>(Streams.java:713)
    at java.util.stream.Streams$ConcatSpliterator$OfPrimitive.<init>(Streams.java:789)
    at java.util.stream.Streams$ConcatSpliterator$OfPrimitive.<init>(Streams.java:785)
    at java.util.stream.Streams$ConcatSpliterator$OfInt.<init>(Streams.java:819)
    at java.util.stream.IntStream.concat(IntStream.java:851)
    at com.jeffreybosboom.projecteuler.util.Primes.primes(Primes.java:22)
    at com.jeffreybosboom.projecteuler.util.Primes$PrimeSpliterator.<init>(Primes.java:32)
    at com.jeffreybosboom.projecteuler.util.Primes$Lambda$1/834600351.get(Unknown Source)
    at java.util.stream.StreamSpliterators$DelegatingSpliterator.get(StreamSpliterators.java:513)
    at java.util.stream.StreamSpliterators$DelegatingSpliterator.estimateSize(StreamSpliterators.java:536)
    at java.util.stream.Streams$ConcatSpliterator.<init>(Streams.java:713)
    at java.util.stream.Streams$ConcatSpliterator$OfPrimitive.<init>(Streams.java:789)
    at java.util.stream.Streams$ConcatSpliterator$OfPrimitive.<init>(Streams.java:785)
    at java.util.stream.Streams$ConcatSpliterator$OfInt.<init>(Streams.java:819)
    at java.util.stream.IntStream.concat(IntStream.java:851)
    at com.jeffreybosboom.projecteuler.util.Primes.primes(Primes.java:22)

Wie kann ich Streams so träge verketten, dass ein Stream eine andere Kopie von sich selbst in seiner Implementierung verwenden kann?

Antworten auf die Frage(2)

Ihre Antwort auf die Frage