Java8流-堆栈溢出异常

Java8流-堆栈溢出异常,java,java-8,java-stream,reduce,Java,Java 8,Java Stream,Reduce,运行以下代码示例以结束: “线程”main“java.lang.StackOverflowerr”中的异常 import java.util.stream.IntStream; 导入java.util.stream.stream; 公共类测试流{ 公共静态void main(字符串[]args){ Stream reducedStream=IntStream.range(0,15000) .mapToObj(Abc::新建) .减少( 流(“测试”) ,(str,abc)->abc.proces

运行以下代码示例以结束:
“线程”main“java.lang.StackOverflowerr”中的异常

import java.util.stream.IntStream;
导入java.util.stream.stream;
公共类测试流{
公共静态void main(字符串[]args){
Stream reducedStream=IntStream.range(0,15000)
.mapToObj(Abc::新建)
.减少(
流(“测试”)
,(str,abc)->abc.process(str)
,(a,b)->{抛出新的非法状态异常();}
);
System.out.println(reducedStream.findFirst().get());
}
私有静态类Abc{
公共Abc(内部id){
}
公共流处理(流批处理){
返回batch.map(this::doNothing);
}
私有字符串不带任何内容(字符串测试){
回归试验;
}
}
}

到底是什么导致了这个问题?这段代码的哪一部分是递归的,为什么?

您的代码不是递归循环的。您可以使用较小的整数范围(即1或100)进行测试。在您的情况下,是实际堆栈大小限制导致了问题。正如一些评论中所指出的,这是流处理的方式

流上的每次调用都会围绕原始包装流创建一个新的包装流。“findFirst()”方法向上一个流请求元素,而上一个流又向上一个流请求元素。因为流不是真正的容器,只是结果元素上的指针

包装器爆炸发生在reduce方法“acculator”(str,abc)->abc.process(str)中。该方法的实现在前一个操作的结果(str)上创建一个新的流包装器,并将其输入到下一个迭代中,在结果(result(str))上创建一个新的包装器。因此,累加机制是一个包装器(递归),而不是一个附加器(迭代)。因此,创建实际(展平)结果的新流而不是参考潜在结果将阻止爆炸,即

public Stream<String> process(Stream<String> batch) {
        return Stream.of(batch.map(this::doNothing).collect(Collectors.joining()));
    }
公共流处理(流批处理){
返回Stream.of(batch.map(this::doNothing.collect)(Collectors.joining());
}
这个方法只是一个示例,因为您的原始示例没有任何意义,因为它什么都不做,这个示例也不做。这只是一个例证。它基本上将map方法返回的流的元素展平为单个字符串,并在这个具体字符串上而不是流本身上创建一个新流,这与原始代码不同


您可以使用'-Xss'参数来调整stacksize,该参数定义每个线程的堆栈大小。默认值取决于平台,另请参见此问题,但增加时请小心,此设置适用于所有线程

为什么要将一个流缩减为另一个流?问题来自对
map
的15000个调用。在内部,它必须以某种方式将它们联系起来。您可以不使用
Abc
IntStream.range(0,15000).boxed().reduce(Stream.of(“Test”),(str,Abc)->str.map(s->s),(a,b)->{抛出新的非法状态异常();}
StackOverflowerError中的堆栈跟踪应该显示方法之间调用的循环。请在您的帖子中列出相关部分。更简短的是:
Stream st=Stream.of(“Test”);for(int i=0;i<15000;i++)st=st.map(s->s);st.findFirst()
我不知道我们是否可以将此定义为一个bug。每个映射操作都会创建一个临时对象来包装当前流。然后当遍历开始时,它必须经过所有这些间接操作。而且它似乎太多了。注意:此错误也在这张票证中提到::由Paul Sandoz编写。注意,这不会停止所有stack溢出,并且可以通过其他方式创建堆栈溢出,例如创建一个非常长的管道,比如说Stream.of(1),使用终端操作(例如toArray()创建许多map(Function.identity())操作。在这方面,在文档中添加一些实现说明可能是合适的。谢谢。我需要花更多的时间调试Java流。这样做:
返回Stream.of(batch.map(This::doNothing.collector(Collectors.joining());
但这是非常奇怪的代码行:)
public Stream<String> process(Stream<String> batch) {
        return Stream.of(batch.map(this::doNothing).collect(Collectors.joining()));
    }