最近在我的工作中,我不得不顺序处理一堆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的“困难方式”之外,还有一个简单的选择吗?
答案 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();
}
如果您需要进一步的流水线操作,那么返回连接流将是个好主意。否则你可以映射并收集结果。