How to make flow piping easier

I think my code needs improvement. I use the object in allSummaryTSTLog

both threads filter()

and map()

so I have to call twice 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());
}

      

How can I call File.listFiles()

to create allSummaryTSTLog

just one time?

-1


source to share


2 answers


It is not uncommon to filter flow items based on some computation and then reuse the result of that computation at a later stage in the pipeline. Of course, you could double-check this result at a later stage in the pipeline, but it makes perfect sense if you don't want to. If the stream items themselves cannot store these computed results, and you do not want to compromise them, you need to create a helper class to carry the original items and computed results downstream.

I looked at the data that was being used in the pipeline and here is the helper class I ran into:

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

      

Please note that I cheated a little here for brevity. First, instead of checking explicitly isDirectory

, I use the fact that it File.listFiles

returns null if File

not a directory. I also haven't bothered to differentiate the case where the given filename is not a directory, but the case where it is a directory but doesn't contain matching files. (This may be important to you, however, I'm not sure.)



Finally, I make the path to the filename, which is split here in the constructor so that the last component of the name is available when needed. The split requires a local variable. If the splitting was done at a later stage in the pipeline, the local variable would force the use of the lambda operator instead of the lambda expression, which would make things a lot more cumbersome. The trade-off is that we could complete the pathname for the files that end up being filtered out, but that doesn't seem like an overhead.

With this helper class, 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 stage of the pipeline, we map the incoming stream to an instance of our helper class. Subsequent stages can use the data in the helper class without having to compromise it.

+1


source


It's 2015. DO NOT USE File

.

Also, there is no clue as to what you want to do (what are you MyFilter

doing? What is Test

?).

Use something like this:



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
}

      

Since your post contains zero clues as to what you want to do, this is the best thing to do.

How to get a Path

, see Paths.get()

. And the java.nio.file document in general.

-1


source







All Articles