如何使流管道更简单

时间:2015-03-18 03:43:34

标签: java lambda java-8 java-stream

我认为我的代码需要改进。我在流的allSummaryTSTLogfilter()阶段都使用了对象map(),所以我必须两次调用File.listFiles

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一次?

2 个答案:

答案 0 :(得分:1)

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

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

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如果File不是目录则返回null的事实。我也不愿意区分给定文件名不是目录的情况与它是目录但不包含匹配文件的情况。 (但这对你来说可能很重要;我不确定。)

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

使用此助手类,可以按如下方式重写管道:

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

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

答案 1 :(得分:-1)

这是2015年。不要使用File

此外,根本不知道你想做什么(你MyFilter做什么?Test是什么?)。

使用类似的东西:

final BiPredicate<Path, BasicFileAttributes> predicate = (path, attrs) -> {
    return attrs.isRegularFile()
        && path.getFileName().toString().equals(something);
};

try (
    final Stream<Path> stream = Files.find(baseDir, filter);
) {
    // work with the stream
}

由于你的帖子中没有关于你想做什么的线索,这是最好的。

关于如何获取Path,请参阅Paths.get()。和java.nio.file的文档一般。