如何使流管道更简单 [英] How to make stream pipeline simpler

查看:94
本文介绍了如何使流管道更简单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我认为我的代码需要改进。我在流的过滤器() map() allSummaryTSTLog c $ c>阶段,所以我必须两次致电 File.listFiles

I think my code needs improvement. I use object allSummaryTSTLog both in the stream's filter() and map() stages, so I have to call File.listFiles twice:

public static List<Test> ParserPath(List<String> allLogPath) {

    FilenameFilter filter = new MyFilter("Summary_TSTLog");

    return allLogPath.parallelStream().filter(path -> {
        File testPath = new File(path);

        if (!testPath.isDirectory()) {
            MyLog.log.info("test path : [" + path + "] is not exist, continue");
            return false;
        }

        File[] allSummaryTSTLog = testPath.listFiles(filter);
        if (allSummaryTSTLog == null || allSummaryTSTLog.length == 0) {
            MyLog.log.info("test path : [" + path + "] has no Summary_TSTLog files");
            return false;
        }
        return true;
    }).map(path -> {
        String[] nameTempStr = path.split("\\\\");
        String testName = nameTempStr[nameTempStr.length - 1];

        File[] allSummaryTSTLog = new File(path).listFiles(filter);

        return new Test(testName, Arrays.asList(allSummaryTSTLog));
    }).collect(Collectors.toList());
}

如何调用 File.listFiles()只创建 allSummaryTSTLog 一次?

推荐答案

希望基于某些计算过滤流元素,然后在稍后的流水线阶段重用该计算的结果并不罕见。你当然可以在后面的管道阶段重新计算这个结果,但如果你不想这样做,这是非常合理的。如果流元素本身不能存储那些计算结果,并且您不想重新计算它们,则必须创建一个辅助类来承载流中的原始元素和计算结果。

It's not uncommon to want to filter stream elements based on some computation, and then reuse the result of that computation in a later pipeline stage. You could of course recompute that result in the later pipeline stage, but it's quite reasonable if you don't want to do that. If the stream elements themselves can't store those computed results, and you don't want to recompute them, you have to create a helper class to carry the original elements and computed results down the stream.

我查看了管道中使用的数据,这是我提出的帮助类:

I looked at the data that's used in the pipeline, and here's the helper class that I came up with:

static class FileInfo {
    final String fullName;
    final String lastName;
    final File[] allSummaryTSTLog;

    FileInfo(String n, FilenameFilter fnf) {
        fullName = n;
        String[] tmp = n.split("\\\\");
        lastName = tmp[tmp.length - 1];
        allSummaryTSTLog = new File(n).listFiles(fnf);
    }
}

请注意,为了简洁,我在这里作了一点作弊。首先,我没有明确地检查 isDirectory ,而是利用 File.listFiles 返回null的事实文件不是目录。我也没有打算区分给定文件名不是目录的情况与它是目录但不包含匹配文件的情况。 (但这对你来说可能很重要;我不确定。)

Note that I've cheated a bit here for brevity. First, instead of checking for isDirectory explicitly, I take advantage of the fact that File.listFiles returns null if the File is not a directory. I also haven't bothered to differentiate the case where the given filename is not a directory vs. the case where it is a directory but contains no matching files. (That might be significant for you, however; I'm not sure.)

最后,我在构造函数中执行文件名路径拆分,以便最后一个组件必要时可以使用名称。拆分需要一个局部变量。如果拆分是在稍后的管道阶段完成的,那么局部变量将强制使用语句lambda而不是表达式lambda,这会使事情变得更加繁琐。权衡的是,我们最终可能会对最终被过滤掉的文件进行路径名拆分,但这看起来似乎并不过多。

Finally, I do the filename path splitting here in the constructor so that last component of the name is available when necessary. The splitting requires a local variable. If the splitting were done in a later pipeline stage, the local variable would force the use of a statement lambda instead of an expression lambda, which would make things a lot more cumbersome. The tradeoff is that we might end up doing pathname splitting for files that end up being filtered away, but that doesn't seem like an excessive expense.

使用此助手类在适当的位置,管道可以按如下方式重写:

With this helper class in place, the pipeline can be rewritten as follows:

 static List<Test> parserPath(List<String> allLogPath) {
    FilenameFilter filter = new MyFilter("Summary_TSTLog");
    return allLogPath.parallelStream()
        .map(str -> new FileInfo(str, filter))
        .filter(fi -> fi.allSummaryTSTLog != null && fi.allSummaryTSTLog.length > 0)
        .map(fi -> new Test(fi.lastName, Arrays.asList(fi.allSummaryTSTLog)))
        .collect(toList());
}

在第一个管道阶段,我们将传入的流元素映射到一个实例我们的助手班。后续阶段可以使用辅助类中的数据,而无需重新计算它。

In the first pipeline stage, we map the incoming stream element into an instance of our helper class. The subsequent stages can use the data in the helper class without having to recompute it.

这篇关于如何使流管道更简单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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