实际任务是能够通过读取文件返回流,但这样做时不会将整个文件(或解析的集合)完全加载到内存中。该流的目的可能稍后确定——例如保存到DB。开发人员将拥有反序列化流的句柄(而不是反序列化集合)。
这样做的问题是,不能保证文件中的一行等于一个MyEntity对象(在这种情况下,我可以使用本文:http://blog.codeleak.pl/2014/05/parsing-file-with-stream-api-in-java-8.html)
一般情况下,可能会遇到这样的情况:提供一个输入流,需要返回一个通过将可变数量的输入流项映射到一个输出流项构造的输出流。
所以,到目前为止,我的解决方案是通过使用一个供应商,像这样:public class Parser{
public Stream<MyEntity> parse(final InputStream stream) {
return Stream.generate(new AggregatingSupplier(stream));
}
private class AggregatingSupplier implements Supplier<MyEntity> {
private final Scanner r;
public AggregatingSupplier(final InputStream source) {
this.r= new Scanner(source);
}
@Override
public MyEntity get() {
MyEntity re=new MyEntity();
while (r.hasNextLine() &&!re.isComplete()){
String line=r.nextLine();
// ... do some processing
}
return re;
}
}
}
这种方法的问题是使用stream获得的流。生成是无限的。没有停止条件。抛出异常(在某种程度上)是有效的。或者选择一个完全不同的(经典的)方法。
考虑实现您的自定义Spliterator
而不是Supplier
。它不像一开始看起来那么令人生畏(通过检查Spliterator
接口),因为有Spliterators.AbstractSpliterator
基类,这使得它很容易:只需提供tryAdvance()
,它看起来基本上与你现在在Supplier
中所拥有的相同。
停止条件变得简单:让tryAdvance()
返回false
。
使用我的StreamEx库可能会简单一些,因为它具有杀手级特性,可以根据指定的条件部分地减少组合几个相邻元素的流。例如,您可以这样做:
public Stream<MyEntity> parse(final InputStream stream) throws IOException {
return StreamEx.ofLines(new InputStreamReader(stream))
.groupRuns((a, b) -> !isEndOfEntry(a))
.map(strings -> createMyEntityFromListOfStrings(strings));
}
groupRuns
方法接受应用于相邻行对的BiPredicate
,如果这些行属于同一组,则应返回true。如果您有标记条目最后一行的特定标记,则可以测试第一个字符串(a
)。或者,如果更容易检测新条目的开始,您可以检查字符串b
。此方法创建StreamEx<List<String>>
,其中元素是分组字符串的列表,因此您可以处理它们以创建MyEntity
对象。如果你不喜欢有一个中间的List
,你可以写Collector
来创建你的MyEntity
,并使用collapse(BiPredicate, Collector)
方法来接受相同的BiPredicate
和任何Collector
来执行部分约简。