【问题标题】:Zip streams for collections of stream throwing stack overflow exception用于流集合的 Zip 流引发堆栈溢出异常
【发布时间】:2022-11-11 02:06:20
【问题描述】:

我有一组流,我试图将它们压缩到一个流中。我正在使用 guava Streams.zip 方法来压缩流。当集合中的流数低于 8000 时它工作正常,高于 8000 时它开始抛出堆栈溢出异常。在本地调试时,我发现堆栈溢出发生在 zip 方法中。它成功压缩到 8000 个流,然后开始抛出异常。我无法找到解决方法或为什么会发生这种情况。需要一些帮助才能找到。番石榴邮政编码在这里https://github.com/google/guava/blame/6d7e326b2cbfba5f19fc67859c0b3d4c45fab63f/guava/src/com/google/common/collect/Streams.java#L318

我尝试了本地调试。将我所有的 lambda 调用转换为 vanlla for loop ,以便确认我们没有递归调用任何东西。最后指出这是由 zip 函数引起的。

源代码:

使用 zip 的合并方法。

private static <T> Stream<T> merge(Stream<T> firstList, Stream<T> secondList) {
            return Streams.zip(firstList, secondList, (first, second) -> {
                if (first == null) {
                    return second;
                }
    
                return first.merge(second);
            });
        }

我这样调用合并方法

Collections.singletonList(inlineList.stream()
                    .reduce(merge)

其中 inline list 是流列表。

例外:

java.lang.StackOverflowError 在 java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) 在 com.google.common.collect.Streams$1.tryAdvance(Streams.java:322) 在 java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) 在 com.google.common.collect.Streams$1.tryAdvance(Streams.java:322) 在 java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) 在 com.google.common.collect.Streams$1.tryAdvance(Streams.java:322) 在 java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) 在 com.google.common.collect.Streams$1.tryAdvance(Streams.java:322) 在 java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) 在 com.google.common.collect.Streams$1.tryAdvance(Streams.java:322) 在 java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) 在 com.google.common.collect.Streams$1.tryAdvance(Streams.java:322) 在 java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) 在 com.google.common.collect.Streams$1.tryAdvance(Streams.java:322) 在 java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) 在 com.google.common.collect.Streams$1.tryAdvance(Streams.java:322) 在 java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) 在 com.google.common.collect.Streams$1.tryAdvance(Streams.java:322) 在 java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681)

【问题讨论】:

  • StackOverflow 本身并不意味着递归。这只是意味着你有太多的嵌套调用,可能需要简化你的代码。
  • 是的,我只是重构了代码以避免这种可能性。但我很确定我从 zip 方法内部得到了堆栈溢出。
  • 它可能发生在任何地方。调用堆栈之所以称为堆栈,是因为它是一个调用堆栈。
  • 我对你说的话感到困惑。 Streams.zip() 接受流。您如何使用它压缩 8000 多个流?与往常一样,您应该阅读有关如何提问的帮助。提供一个可运行最小的例子总是最好的。
  • 如果您以堆叠调用的方式减少它们,那将解释异常。但就像 Gene 说的,请分享你的代码。

标签: java java-stream stack-overflow guava


【解决方案1】:

问题是zip() 返回一个委托给firstListsecondList 的流,因此对结果流的每次调用都会触发对底层流的嵌套调用。获取您的结果并将其与下一个流一起压缩,现在您的堆栈深度为 3 级。做 8000 次,you can see 为什么它可能会产生堆栈溢出。

我怀疑这是XY Problem,并且有一种更简洁的方法可以实现您想要的,而不涉及压缩流。但是在问题的参数范围内,一种解决方案是编写一个可以处理的拉链n同时流,而不是一一合并:

static <T> Stream<T> merge(List<Stream<T>> streams, BinaryOperator<T> mergeFunction) {
    List<Iterator<T>> iters = streams.stream()
            .map(Stream::iterator)
            .collect(Collectors.toList());

    return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, 0) {
        @Override
        public boolean tryAdvance(Consumer<? super T> action) {
            Optional<T> next = iters.stream()
                    .filter(Iterator::hasNext)
                    .map(Iterator::next)
                    .reduce(mergeFunction);

            next.ifPresent(action);
            return next.isPresent();
        }
    }, false);
}

【讨论】:

  • 感谢您的回答,我没有按照您提到的部分“返回一个委托给 firstList 和 secondList 的流”。这是什么意思?另外,我故意截断了传递给 zip 方法的双功能的详细信息。现在阅读您的解决方案,我看起来很傻。我在问题中添加了其他详细信息。基本上我需要合并流中的两个项目。
  • 我的意思是它返回一个包装流,该流必须将其调用转发到您的流。我更新了代码以接受自定义合并功能。
猜你喜欢
  • 1970-01-01
  • 2010-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-07
  • 1970-01-01
相关资源
最近更新 更多