连接流或可迭代对象:StackOverflow
Concatenate Streams or Iterables: StackOverflow
最近在我的工作中,我不得不按顺序处理一堆 xml 文件。在这种情况下,我编写了一种树遍历,因此每个 xml 文件都变成了一个 Iterator。
后来程序不关心哪个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));
有点令人惊讶的是,这也会引发 Whosebug。最后我不得不通过实现 Iterator 手动进行连接,最终按预期工作。
为什么当有足够的数据时,这些标准库的连接方法会失败?毕竟,Streams 和 Iterables 旨在处理无限输入。除了 "hard way" 实现 Iterator 之外,还有其他简单的替代方法吗?
要连接大量流,请使用 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));
也许你可以提取如下方法:
private <T> Stream<T> flatten(final Collection<T> ... collections) {
return Stream.of(collections).map(Collection::stream).reduce(Stream::concat).get();
}
如果您需要进一步的流水线操作,返回串联流是个好主意。否则你可以映射并收集结果。
最近在我的工作中,我不得不按顺序处理一堆 xml 文件。在这种情况下,我编写了一种树遍历,因此每个 xml 文件都变成了一个 Iterator
后来程序不关心哪个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));
有点令人惊讶的是,这也会引发 Whosebug。最后我不得不通过实现 Iterator 手动进行连接,最终按预期工作。
为什么当有足够的数据时,这些标准库的连接方法会失败?毕竟,Streams 和 Iterables 旨在处理无限输入。除了 "hard way" 实现 Iterator 之外,还有其他简单的替代方法吗?
要连接大量流,请使用 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));
也许你可以提取如下方法:
private <T> Stream<T> flatten(final Collection<T> ... collections) {
return Stream.of(collections).map(Collection::stream).reduce(Stream::concat).get();
}
如果您需要进一步的流水线操作,返回串联流是个好主意。否则你可以映射并收集结果。