Java 8 从输入流读取到输出流,同时聚合项
Java 8 read from an input stream to an output stream, while aggregating items
实际任务是能够通过从文件中读取来 return 流,但这样做不会将整个文件(或已解析的集合)完全加载到内存中。该流的目的可以稍后确定——例如保存到数据库。开发人员将拥有反序列化流的句柄(而不是反序列化集合)。
问题是不能保证文件中的一行等于一个 MyEntity 对象(在这种情况下我可以使用这篇文章:http://blog.codeleak.pl/2014/05/parsing-file-with-stream-api-in-java-8.html)
通常,人们可能会发现这样一种情况,即提供一个输入流,需要return一个输出流,该输出流通过将可变数量的输入流项映射到一个输出流项来构造。
到目前为止,我的解决方案是使用供应商,如下所示:
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.generate 获得的流是无限的。没有停止条件。抛出异常有效(有点)。或者选择完全不同的(经典)方法。
考虑实施您的自定义 Spliterator
,而不是 Supplier
。它不像最初看起来那么令人生畏(通过检查 Spliterator
界面),因为有 Spliterators.AbstractSpliterator
基础 class 这使得它变得非常容易:只需提供 tryAdvance()
,它看起来与 Supplier
.
中的内容基本相同
停止条件变得简单:只需让 tryAdvance()
return 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
method accepts the BiPredicate
which is applied to the pair of adjacent lines and should return true if these lines are part of the same group. If you have specific marker which marks the last line of the entry, you can test the first string (a
) for this. Alternatively if it's easier to detect the start of the new entry, you can check the string b
. This method creates StreamEx<List<String>>
which elements are lists of grouped strings, so you can process them to create your MyEntity
object. If you don't like to have an intermediate List
, you can write the Collector
which creates your MyEntity
and use the collapse(BiPredicate, Collector)
方法接受相同的 BiPredicate
和任何 Collector
来执行部分缩减。
实际任务是能够通过从文件中读取来 return 流,但这样做不会将整个文件(或已解析的集合)完全加载到内存中。该流的目的可以稍后确定——例如保存到数据库。开发人员将拥有反序列化流的句柄(而不是反序列化集合)。
问题是不能保证文件中的一行等于一个 MyEntity 对象(在这种情况下我可以使用这篇文章:http://blog.codeleak.pl/2014/05/parsing-file-with-stream-api-in-java-8.html)
通常,人们可能会发现这样一种情况,即提供一个输入流,需要return一个输出流,该输出流通过将可变数量的输入流项映射到一个输出流项来构造。
到目前为止,我的解决方案是使用供应商,如下所示:
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.generate 获得的流是无限的。没有停止条件。抛出异常有效(有点)。或者选择完全不同的(经典)方法。
考虑实施您的自定义 Spliterator
,而不是 Supplier
。它不像最初看起来那么令人生畏(通过检查 Spliterator
界面),因为有 Spliterators.AbstractSpliterator
基础 class 这使得它变得非常容易:只需提供 tryAdvance()
,它看起来与 Supplier
.
停止条件变得简单:只需让 tryAdvance()
return 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
method accepts the BiPredicate
which is applied to the pair of adjacent lines and should return true if these lines are part of the same group. If you have specific marker which marks the last line of the entry, you can test the first string (a
) for this. Alternatively if it's easier to detect the start of the new entry, you can check the string b
. This method creates StreamEx<List<String>>
which elements are lists of grouped strings, so you can process them to create your MyEntity
object. If you don't like to have an intermediate List
, you can write the Collector
which creates your MyEntity
and use the collapse(BiPredicate, Collector)
方法接受相同的 BiPredicate
和任何 Collector
来执行部分缩减。