我正在尝试实现具有以下签名的方法:
public static <A,B> Pair<Stream<A>, Stream<B>> flatten(Iterator<Pair<Stream<A>, Stream<B>>> iterator);
该方法的目标是将每种流类型展平为单个流并将输出包装成一对。我只有一个 Iterator(不是 Iterable)并且我不能改变方法签名,所以我必须在一次迭代中执行扁平化。
我目前最好的实现是
public static <A,B> Pair<Stream<A>, Stream<B>> flatten(Iterator<Pair<Stream<A>, Stream<B>> iterator) {
Stream<A> aStream = Stream.empty();
Stream<B> bStream = Stream.empty();
while(iterator.hasNext()) {
Pair<Stream<A>, Stream<B>> elm = iterator.next();
aStream = Stream.concat(aStream, elm.first);
bStream = Stream.concat(bStream, elm.second);
}
return Pair.of(aStream, bStream);
}
虽然这在技术上是正确的,但我对此并不十分满意,原因有二:
- Stream.concat warns against doing this kind of thing because it may lead to a StackOverflowError .
- 从风格上讲,如果可能的话,我宁愿它是纯函数式的,而不是必须遍历迭代器并在整个过程中重新分配流。
感觉像Stream#flatMap应该适合这里(在使用 Guava's Streams.stream(Iterator) 将输入 Iterator 转换为 Stream 之后,但由于中间的 Pair 类型,它似乎不起作用。
一个额外的要求是任何迭代器/流都可能非常大(例如,输入可以包含从一对非常大的流到许多一个项目流的任何地方)所以理想的解决方案不应该包含收集结果存入内存集合。
最佳答案
Guava 的 Streams.stream
并不神奇,它实际上在内部只是:
StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
所以可能不需要将其链接到您的方法,而您可以直接使用它。
您可以为此使用 Stream.Builder
:
public static <A, B> Pair<Stream<A>, Stream<B>> flatten(Iterator<Pair<Stream<A>, Stream<B>>> iterator) {
Stream.Builder<Stream<A>> builderA = Stream.builder();
Stream.Builder<Stream<B>> builderB = Stream.builder();
iterator.forEachRemaining(pair -> {
builderA.add(pair.first);
builderB.add(pair.second);
});
return Pair.of(builderA.build().flatMap(Function.identity()), builderB.build().flatMap(Function.identity()));
}
关于Java flatmap Iterator<Pair<Stream<A>, Stream<B>>> 到 Pair<Stream<A>, Stream<B>>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44735562/