Java8流线和终端线上的动作聚合 [英] Java8 stream lines and aggregate with action on terminal line

查看:101
本文介绍了Java8流线和终端线上的动作聚合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题



如何在处理有序流的最后一项之后但在它关闭之前执行某个操作?
此操作应该能够在流管道中注入零个或多个项目。



上下文



我有一个非常大的表格文件:

  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) ;
返回null;
} else {
结果result = builder.build()
builder.parseMasterRef(line);
返回结果;
}
})
//消除null
.filter(Objects :: nonNull)
//对结果进行一些处理
.map(Utils :: doSomething)
// terminal op
.forEachOrdered(System.out :: println);
}

使用 forEach 这是一个坏主意...好方法是使用 forEachOrdered



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



因此我想知道如何在行处理结束时的流。

解决方案

你的问题听起来很混乱。当显式调用 close()方法或使用try-with-resources构造时,将关闭Stream。在您的代码示例中,流根本不会关闭。要在流关闭之前执行自定义操作,您可以在try-with-resource语句结束时写一些内容。



在您的情况下,您似乎想要将一些虚假条目连接到流。这里有 Stream.concat()方法:

 流。 concat(Files.lines(path),Stream.of(MASTER))
.map(...)//执行所有其他步骤






最后请注意我的 StreamEx 库提供了部分缩减方法,可以很好地解析多行条目。使用 StreamEx.groupRuns() ,它通过给定的 BiPredicate 将相邻元素组合到中间列表中:

  public void process(Path path){
StreamEx.of(Files.lines(path))
.groupRuns((line1,line2) - > line2.charAt(0)=='')
//现在流元素是List< String>从MASTER开始并在
.map之后使用
//所有subref字符串(记录 - > {
MyBuilder builder = new MyBuilder();
builder.parseMasterRef(记录。 get(0));
record.subList(1,record.size())。forEach(builder :: parseSubRef);
return record.build();
})
//消除null
.filter(Objects :: nonNull)
//对结果进行一些处理
.map(Utils :: doSomething)
// terminal op
.forEach(System.out :: println);
}

现在您不需要使用副作用。


Question

How to execute an action after the last item of an ordered stream is processed but before it's closed ? This Action should be able to inject zero or more items in the stream pipe.

Context

I've got a very large file of the form :

MASTER_REF1
    SUBREF1
    SUBREF2
    SUBREF3
MASTER_REF2
MASTER_REF3
    SUBREF1
    ...

Where SUBREF (if any) is applicable to MASTER_REF and both are complex objects (you can imagine it somewhat like JSON).

On first look I tried something like :

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);
}

[EDIT] using forEach here was a bad idea ... the good way was to use forEachOrdered

But, for obvious reasons, the last item is never appended to the stream : it is still being built.

Therefore I'm wondering how to flush it in the stream at the end of line processing.

解决方案

Your question sounds confusing. The Stream is closed when the close() method is called explicitly or when try-with-resources construct is used. In your code sample the stream is not closed at all. To perform custom action before the stream is closed, you can just write something at the end of try-with-resource statement.

In your case it seems that you want to concatenate some bogus entry to the stream. There's Stream.concat() method to do this:

Stream.concat(Files.lines(path), Stream.of("MASTER"))
      .map(...) // do all your other steps


Finally note that my StreamEx library which enhances the Stream API provides partial reduction methods which are good to parse multi-line entries. The same thing can be done using StreamEx.groupRuns() which combines adjacent elements into intermediate list by given 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);
}

Now you don't need to use side-effect operations.

这篇关于Java8流线和终端线上的动作聚合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆