Java8流线和终端线上的动作聚合

时间:2015-11-23 09:53:11

标签: java java-8 java-stream

问题

如何在处理有序流的最后一项之后但在它关闭之前执行操作? 此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

但是,由于显而易见的原因,最后一项永远不会附加到流中:它仍在构建中。

因此,我想知道如何在流处理结束时在流中刷新它。

2 个答案:

答案 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);
                }
            })
            ...