Java 如何懒洋洋地连接流?

Java 如何懒洋洋地连接流?,java,java-8,java-stream,Java,Java 8,Java Stream,我正在尝试实现一个流,它在实现中使用自身的另一个实例。流前面有几个常量元素(带有IntStream.concat),因此只要连接的流延迟创建非常量部分,这应该可以工作。我认为使用with IntStream.concat(它)应该足够懒惰,只在需要元素时创建第二个拆分器,但即使创建流(不计算流)也会溢出堆栈。我如何懒洋洋地连接流 我正在尝试将流素数筛从移植到Java中。此筛选使用自身的另一个实例(ps=pulated\u sieve(),在Python代码中)。如果我将最初的四个常量元素(yi

我正在尝试实现一个流,它在实现中使用自身的另一个实例。流前面有几个常量元素(带有IntStream.concat),因此只要连接的流延迟创建非常量部分,这应该可以工作。我认为使用with IntStream.concat(它)应该足够懒惰,只在需要元素时创建第二个拆分器,但即使创建流(不计算流)也会溢出堆栈。我如何懒洋洋地连接流


我正在尝试将流素数筛从移植到Java中。此筛选使用自身的另一个实例(
ps=pulated\u sieve()
,在Python代码中)。如果我将最初的四个常量元素(
yield 2;yield 3;yield 5;yield 7;
)分解为它们自己的流,那么将生成器实现为拆分器很容易:

/**
 * 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;
    }
}
public static IntStream primes() {
    return IntStream.concat(IntStream.of(2, 3, 5, 7),
            StreamSupport.intStream(new PrimeSpliterator()));
}
public static IntStream primes() {
    return IntStream.concat(IntStream.of(2, 3, 5, 7),
            StreamSupport.intStream(PrimeSpliterator::new, PrimeSpliterator.CHARACTERISTICS, false));
}
调用primes()会导致StackOverflowerError,因为primes()总是实例化PrimeSpliterator,但PrimeSpliterator的字段初始值设定项总是调用primes()。但是,StreamSupport.intStream中有一个过载,需要供应商,这应该允许延迟创建PrimeSpliterator:

/**
 * 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;
    }
}
public static IntStream primes() {
    return IntStream.concat(IntStream.of(2, 3, 5, 7),
            StreamSupport.intStream(new PrimeSpliterator()));
}
public static IntStream primes() {
    return IntStream.concat(IntStream.of(2, 3, 5, 7),
            StreamSupport.intStream(PrimeSpliterator::new, PrimeSpliterator.CHARACTERISTICS, false));
}
然而,我得到了一个具有不同回溯的StackOverflower错误(当它重复时被修剪)。请注意,递归完全在对primes()的调用中——终端操作迭代器()永远不会在返回的流上调用

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)
线程“main”java.lang.StackOverflower中出现异常 位于java.util.stream.StreamSpliterators$DelegatingSpliterator$OfInt.(StreamSpliterators.java:582) 位于java.util.stream.IntPipeline.LazySpipelator(IntPipeline.java:155) 位于java.util.stream.IntPipeline$Head.lazySpliterator(IntPipeline.java:514) 位于java.util.stream.AbstractPipeline.spliterator(AbstractPipeline.java:352) 位于java.util.stream.IntPipeline.spliterator(IntPipeline.java:181) 位于java.util.stream.IntStream.concat(IntStream.java:851) 位于com.jeffreybosboom.projecteuler.util.Primes.Primes(Primes.java:22) 在com.jeffreybosboom.projecteuler.util.Primes$PrimeSpliterator上。(Primes.java:32) 位于com.jeffreybosboom.projecteuler.util.Primes$$Lambda$1/834600351.get(未知来源) 位于java.util.stream.StreamSpliterators$DelegatingSpliterator.get(StreamSpliterators.java:513) 位于java.util.stream.StreamSpliterators$DelegatingSpliterator.estimateSize(StreamSpliterators.java:536) 位于java.util.stream.Streams$ConcatSpliterator。(Streams.java:713) 位于java.util.stream.Streams$ConcatSpliterator$of primitive.(Streams.java:789) 位于java.util.stream.Streams$ConcatSpliterator$of primitive.(Streams.java:785) 位于java.util.stream.Streams$ConcatSpliterator$OfInt.(Streams.java:819) 位于java.util.stream.IntStream.concat(IntStream.java:851) 位于com.jeffreybosboom.projecteuler.util.Primes.Primes(Primes.java:22) 在com.jeffreybosboom.projecteuler.util.Primes$PrimeSpliterator上。(Primes.java:32) 位于com.jeffreybosboom.projecteuler.util.Primes$$Lambda$1/834600351.get(未知来源) 位于java.util.stream.StreamSpliterators$DelegatingSpliterator.get(StreamSpliterators.java:513) 位于java.util.stream.StreamSpliterators$DelegatingSpliterator.estimateSize(StreamSpliterators.java:536) 位于java.util.stream.Streams$ConcatSpliterator。(Streams.java:713) 位于java.util.stream.Streams$ConcatSpliterator$of primitive.(Streams.java:789) 位于java.util.stream.Streams$ConcatSpliterator$of primitive.(Streams.java:785) 位于java.util.stream.Streams$ConcatSpliterator$OfInt.(Streams.java:819) 位于java.util.stream.IntStream.concat(IntStream.java:851) 位于com.jeffreybosboom.projecteuler.util.Primes.Primes(Primes.java:22)
如何将流延迟连接到允许流在其实现中使用其自身的另一个副本?

您显然假设streams API将其延迟保证扩展到拆分器的实例化;这是不对的。它希望能够在实际消耗开始之前的任何时候实例化流的拆分器,例如,仅仅为了找出流的特征和报告的大小。消费仅通过调用
trySplit
tryAdvance
foreachrestain
开始

记住这一点,您将在需要之前初始化延迟的筛选。在
tryAdvance
中的
else if
部分之前,您无法使用其任何结果。因此,将代码移动到最后一个可能的时刻,以确保正确性:

@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 (postponedSieve == null) {
              postponedSieve = primes().iterator();
              postponedSieve.nextInt();
              this.p = postponedSieve.nextInt();
              this.q = p*p;
            }
            if (c < q) {
              action.accept(c);
              return true; //continue

您可能会发现,这会给您带来所需的任意多的惰性。

@8472它在构造函数中高级了两次,因此我不知道如何惰性地初始化它。(考虑到IntStream.concat是如何被记录为懒惰的,我认为这个问题仍然有效。)它不属于懒惰流,但是
x->x+2*p
很可能是一个bug,因为
p
是一个成员变量,在计算lambda之前可能会更改。它没有忽略它,而是将它放在不同的位置。不,这是我发布的第一个也是唯一一个版本的代码。你有没有考虑过使用成语
Stream.of(stream1,stream2)。flatMap(identity())
,它在语义上等同于
concat
,但可能更懒惰?@tagirvalev Flatmapping一个流供应商流应该可以解决这个问题(如我编辑的示例)。传播报告的大小比较困难,因为这是一个鸡和蛋的问题:在实例化拆分器之前,您无法找到报告的大小。@Marko Topolnik:嗯,每个试图寻找素数但不使用简单循环和
int
s或
biginger的
位集的人。nextProbablePrime
用于较大的值,而
API,似乎想在函数编程中进行练习,而不是寻找简单实用的解决方案。在这方面,
API做的一切都很好