【问题标题】:Concatenate Streams or Iterables: StackOverflow连接流或迭代:StackOverflow
【发布时间】:2016-05-11 19:25:18
【问题描述】:

最近在我的工作中,我不得不按顺序处理一堆 xml 文件。在这种情况下,我编写了一种树遍历,因此每个 xml 文件都变成了一个 Iterator

后来程序不关心哪个 SomeXmlElement 对象来自哪个文件,所以我想将所有 Iterator 连接成一个。

这大致是如何完成连接的(使用 String 而不是 SomeXmlElement):

Stream<String> s = Stream.empty();
for (int i = 0; i < 100000; i++) {
  s = Stream.concat(s, Arrays.asList("1", "2", "3").stream());
}
s.findFirst().ifPresent(System.out::println);

事实证明,这从不打印任何东西,它只是挂了一段时间,最终你会得到堆错误或堆栈溢出。所以我又试了一次,这次用的是番石榴:

Iterable<String> s = Collections.emptyList();
for (int i = 0; i < 100000; i++) {
  s = Iterables.concat(s, Arrays.asList("1", "2", "3"));
}
System.out.println(Iterables.getFirst(s, null));

有点令人惊讶的是,这也会引发 StackOverflow。最后,我不得不通过实现 Iterator 手动进行连接,最终按预期工作。

为什么当有足够的数据时,这些标准库的连接方法会失败?毕竟,Streams 和 Iterables 旨在处理无限的输入。除了实现迭代器的“硬方式”之外,还有其他简单的选择吗?

【问题讨论】:

  • 请注意,documentation of Stream.concat 明确警告了这一点……
  • 确实如此,尽管警告有点模糊。它也不会给你一个线索,而不是做什么。 Guava 根本没有警告。

标签: java guava java-stream


【解决方案1】:

要连接大量流,请使用flatMap。在您的示例中,您将像这样使用它:

Stream<String> s = IntStream.range(0, 100000).boxed()
     .flatMap(i -> Stream.of("1", "2", "3"));

对于您的实际问题,假设您有一个带有签名 Stream&lt;SomeXmlElement&gt; parseFile(Path p)Stream&lt;Path&gt; files 的方法,它来自于遍历树。

那么你就可以获得Stream&lt;SomeXmlElement&gt;

Stream<SomeXmlElement> elements = files.flatMap(p -> parseFile(p));

【讨论】:

  • 已接受。那里不应该有循环,这是错误的想法。
【解决方案2】:

也许你可以提取如下方法:

private <T> Stream<T> flatten(final Collection<T> ... collections) {
    return Stream.of(collections).map(Collection::stream).reduce(Stream::concat).get();
}

如果您需要进一步流水线化,返回串联流是个好主意。否则,您可以映射并收集结果。

【讨论】:

  • 这并不能解决OP的问题,即Stream::concat不能用于合并大量流而不会炸毁堆栈。如果您尝试在 100000 个集合上调用您的 flatten 方法,它将失败并返回 StackOverflowError
  • @Misha 我不确定你说的是什么。我能够展平 500000 个集合中的 200 万个对象(对象大小约为 2KB),没有任何问题。内存使用量为 4GB 堆中的约 2.5GB 和约 20MB 元空间(java 1.8_45)。一旦我尝试展平 4-5 百万个对象,程序就会遇到 OOM - GC 开销限制超出错误(不是 SOE)。理论上这是合法的,因为我的示例程序中的 GC 太少了。你试过运行这个吗?
  • 感谢您指出 Lakshitha。我一开始也遇到OOM,比栈溢出还要糟糕。它使整个 JVM 崩溃,不仅是一个线程,而且在多线程程序中,您可能无法弄清楚发生了什么。在较小的测试数据上运行良好后,您的程序将在生产中失败。 concat 方法上应该有一个更大的警告标志。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-12
  • 2012-12-17
  • 2013-07-02
  • 1970-01-01
相关资源
最近更新 更多