forEach java 8 为大数据集抛出 StackOverflow 错误

问题描述

我正在尝试类似 -

        Stream<String> wat = Stream.empty();
        for (long i=0;i<10000000L;i++) {
            Stream<String> yaya = Stream.of("This iis a very biig string lsjdflkjkj lkasdjf lkjdsal");
            wat = Stream.of(wat,yaya).flatMap(Function.identity());
        }

        AtomicInteger i= new AtomicInteger();

        wat.forEach(st-> {
            i.incrementAndGet();
        });

        System.out.println(i);

上面抛出堆栈溢出错误-

java.lang.StackOverflowError: null
    at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:485) ~[?:1.8.0_275]
    at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:272) ~[?:1.8.0_275]
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) ~[?:1.8.0_275]
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) ~[?:1.8.0_275]
    at java.util.stream.AbstractPipeline.wrapAndcopyInto(AbstractPipeline.java:472) ~[?:1.8.0_275]
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[?:1.8.0_275]

经过几次尝试和尝试后,我能够通过使用流列表和使用嵌套 for 循环来解决此问题 -

            List<Stream<String>> listofStream = new ArrayList<>();
            for (long i=0;i<10000000L;i++) {
                Stream<String> yaya = Stream.of("This iis a very biig string lsjdflkjkj lkasdjf lkjdsal");                
                listofStream.add(yaya);
            }

            AtomicInteger i= new AtomicInteger();
            listofStream.forEach(st ->
                            st.forEach(sm -> {
                                i.incrementAndGet();
                            })
            );
            System.out.println(i);

代码不会引发任何堆栈溢出问题。我试图查找各种文章,但无法理解为什么这是有效的,而前者却失败了。请帮忙。

解决方法

您在初始 for 循环中所做的是构建一个非常大的对象树,描述您想要实现的目标。

将有一个 Stream 实现知道它想要对两个值执行 flatMap,其中一个将是一个 Stream 实现,它知道它想要执行一个flatMap 在两个值上,其中一个将是 Stream 实现,它知道它想要对两个值执行 flatMap,其中一个将是 Stream 实现知道它想对两个值执行 flatMap,其中一个将是 Stream 实现,它知道它想对两个值执行 flatMap,其中一个将是一个 Stream 实现,它知道它想要对两个值执行 flatMap,其中一个将是一个 Stream 实现,它知道它想要执行 flatMap在两个值上, ... ,其中一个是空的 Stream 实现。

在内部,它们被实现为彼此的包装器,并在必要时相互调用。

由于每个 Stream 实现都需要调用下一个实现,并且该链有数千个对象深,因此您至少需要如此深的堆栈才能完全执行它(实际上可能需要多个这个数字,因为堆栈中可能还有一些辅助方法/中间方法)。

在您的工作代码中,您只有一个 Stream 包含要迭代的 Stream 对象列表。因此调用深度非常有限:外部流一个接一个地委托给内部流。