如何在处理有序流的最后一项之后但在它关闭之前执行操作? 此Action应该能够在流管道中注入零个或多个项目。
我有一个非常大的文件:
MASTER_REF1
SUBREF1
SUBREF2
SUBREF3
MASTER_REF2
MASTER_REF3
SUBREF1
...
SUBREF(如果有的话)适用于MASTER_REF,两者都是复杂的对象(你可以想象它喜欢 JSON)。
初看起来我尝试过这样的事情:
public void process(Path path){
MyBuilder builder = new MyBuilder();
Files.lines(path)
.map(line->{
if(line.charAt(0)==' '){
builder.parseSubRef(line);
return null;
}else{
Result result = builder.build()
builder.parseMasterRef(line);
return result;
}
})
//eliminate null
.filter(Objects::nonNull)
//some processing on results
.map(Utils::doSomething)
//terminal op
.forEachOrdered(System.out::println);
}
[编辑]使用forEach
这是一个坏主意...好方法是使用forEachOrdered
但是,由于显而易见的原因,最后一项永远不会附加到流中:它仍在构建中。
因此,我想知道如何在流处理结束时在流中刷新它。
答案 0 :(得分:2)
你的问题听起来令人困惑。当显式调用close()
方法或使用try-with-resources构造时,将关闭Stream。在您的代码示例中,流根本不会关闭。要在流关闭之前执行自定义操作,您可以在try-with-resource语句的末尾写一些内容。
在您的情况下,您似乎想要将一些虚假条目连接到流。有Stream.concat()
方法可以做到这一点:
Stream.concat(Files.lines(path), Stream.of("MASTER"))
.map(...) // do all your other steps
最后请注意,增强Stream API的StreamEx库提供了部分缩减方法,可以解析多行条目。使用StreamEx.groupRuns()
可以完成同样的事情,https://www.youtube.com/watch?v=eoGITOPpBfU通过给定BiPredicate
将相邻元素组合到中间列表中:
public void process(Path path){
StreamEx.of(Files.lines(path))
.groupRuns((line1, line2) -> line2.charAt(0) == ' ')
// Now Stream elements are List<String> starting with MASTER and having
// all subref strings after that
.map(record -> {
MyBuilder builder = new MyBuilder();
builder.parseMasterRef(record.get(0));
record.subList(1, record.size()).forEach(builder::parseSubRef);
return record.build();
})
//eliminate null
.filter(Objects::nonNull)
//some processing on results
.map(Utils::doSomething)
//terminal op
.forEach(System.out::println);
}
现在你不需要使用副作用。
答案 1 :(得分:0)
这里的主要问题是你正在有效地传输两种类型的记录,这使得它难以管理,因为流主要用于非结构化数据。
我会预先处理文件数据并将其收集到MasterAndSub
条记录中。然后,groupingBy
字段可以Master
这些。{/ p>
class MasterAndSub {
final String master;
final String sub;
public MasterAndSub(String master, String sub) {
this.master = master;
this.sub = sub;
}
}
/**
* Allows me to use a final Holder of a mutable value.
*
* @param <T>
*/
class Holder<T> {
T it;
public T getIt() {
return it;
}
public T setIt(T it) {
return this.it = it;
}
}
public void process(Path path) throws IOException {
final Holder<String> currentMaster = new Holder<>();
Files.lines(path)
.map(line -> {
if (line.charAt(0) == ' ') {
return new MasterAndSub(currentMaster.getIt(), line);
} else {
return new MasterAndSub(currentMaster.setIt(line), null);
}
})
...