A file instance needs to be constructed inside a lambda expression for FileNameFilter interface

Mutating Algorithm :

Suppose that I have the following directory structure for my test.

test
|
|
---test1
|
|
---test2
|
|
---random.txt

In this structure, test1 and test2 are directories while random.txt is a regular file.

I want to write a function that will give me a list of all subdirectories given a file path.

Attempt 1, works (no lambda expression)

private static ArrayList<String> subDirectories(File pathname) {
    ArrayList<String> subdirectories = new ArrayList<>();

    for(File file : pathname.listFiles()) {
        if(file.isDirectory())
            subdirectories.add(file.getName());
    }

    return subdirectories;
}

This correctly returns test1 and test2 as subdirectories.

Attempt 2: using lambda expression

private static String[] subDirectories(File pathname) {
    return pathname.list((dir, name) -> dir.isDirectory());
}

This result now includes random.txt as a directory, which it is clearly not. Why is the function with lambda giving a different result?

EDIT:

Works only when creating a file instance in the lambda expression:

return pathname.list((dir, name) -> new File(dir, name).isDirectory());

dir is already a File object however, why are we creating a File instance from an existing File instance?

EDIT 2:

After looking at the documentation, the dir parameter always refers to the directory in which the file was found. This is why random.txt is returned as the name of a directory. But I still don't know why creating a new File instance is required.

Naman :

Similar to your existing attempt:

private static List<String> subDirectories(File pathname) {
    return Arrays.stream(pathname.listFiles())
                   .filter(File::isDirectory)
                   .map(File::getName)
                   .collect(Collectors.toList());
}

The reason why the lambda solution lists random.txt is the use of FilenameFilter which uses parameters as:

/**
 * Tests if a specified file should be included in a file list.
 *
 * @param   dir    the directory in which the file was found.
 * @param   name   the name of the file.
 * @return  true if and only if the name should be
 *          included in the file list; false otherwise.
 */
boolean accept(File dir, String name);

as you can relate test is a directory and that's why random.txt is filtered to output.


Edit: Using

return pathname.list((dir, name) -> new File(dir, name).isDirectory());

works since, you've created a file and not a directory with the name random.txt. The reason its not a directory is stated in the javadoc

If parent is the empty abstract pathname then the new File instance is created by converting child into an abstract pathname and resolving the result against a system-dependent default directory.

which in your case doesn't hold true, since the parent directory exists.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=359265&siteId=1