连接流或Iterables:StackOverflow

时间:2016-05-11 19:25:19

标签: java guava java-stream

最近在我的工作中,我不得不顺序处理一堆xml文件。在这种情况下,我编写了一种树形漫游,因此每个xml文件都成为Iterator< SomeXmlElement>。

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

这大致是如何完成连接(使用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手动完成concat,并最终按预期工作。

为什么当有足够的数据时,这些标准库的连接方法会失败?毕竟,Streams和Iterables设计用于处理无限输入。除了实施Iterator的“困难方式”之外,还有一个简单的选择吗?

2 个答案:

答案 0 :(得分:1)

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

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

对于您的实际问题,假设您有一个带有签名Stream<SomeXmlElement> parseFile(Path p)Stream<Path> files的方法来自走树。

然后你可以获得Stream<SomeXmlElement>

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

答案 1 :(得分:0)

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

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

如果您需要进一步的流水线操作,那么返回连接流将是个好主意。否则你可以映射并收集结果。