What is the difference between Files.list and Files.walkFileTree and Files.walk with maxdepth = 1?
Asked Answered
C

4

19

If I want to do something with files only on the first level of the directory, is there a difference between using Files.list(...) or Files.walkFileTree(...) or Files.walk(...)?

Files.walkFileTree(directory, Collections.emptySet(), 1, new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        doSomething(file);
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) {
        // log exc
        return FileVisitResult.CONTINUE;
    }
});

versus

Files.list(directory)
    .forEach(path -> {
        try {
            doSomething(path);
        } catch (IOException exc) {
            // log exc
        }
    });

versus

Files.walk(directory, 1)
    .forEach(path -> {
        try {
            doSomething(path);
        } catch (IOException exc) {
            // log exc
        }
});
Chromaticness answered 16/6, 2016 at 11:38 Comment(0)
C
21

Using following code as test, I got the hang of the issue. The main difference between walk* and list is that list(dir) gives a stream of files in the directory dir, while both walk* method walk the subtree of its argument including the root of subtree—the directory itself.

The difference between walk and walkFileTree is that they supply different interfaces for walking the tree: walkFileTree takes FileVisitor, walk gives Stream<Path>.

public class FilesTest {
    public static void main(String[] args) {
        final String pwd = System.getProperty("user.dir");
        System.out.println("Working Directory = " + pwd);
        Path dir = Paths.get(pwd);
        System.out.println("Files.walk");
        try (Stream<Path> stream = Files.walk(dir, 1)) {
            stream.forEach(path -> FilesTest.doSomething("walk", path));
        } catch (IOException e) {
            logException("walk", e);
        }
        System.out.println("Files.walkFileTree");
        try {
            Files.walkFileTree(dir, Collections.emptySet(), 1, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    doSomething("visitFile", file);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    logException("visitFile", exc);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            logException("walkFileTree", e);
        }
        System.out.println("Files.list");
        try (Stream<Path> stream = Files.list(dir)) {
            stream.forEach(path -> FilesTest.doSomething("dir", path));
        } catch (IOException e) {
            logException("dir", e);
        }
    }

    private static void logException(String title, IOException e) {
        System.err.println(title + "\terror: " + e);
    }

    private static void doSomething(String title, Path file) {
        System.out.println(title + "\t: " + file);
    }
}
Chromaticness answered 16/6, 2016 at 13:39 Comment(1)
> "Skip the first element of the stream to ignore the root path" (List only folders of certain depth using Java 8 streams) https://mcmap.net/q/666997/-list-only-folders-of-certain-depth-using-java-8-streamsBeeves
K
4

Files.list simply delegates to Files.newDirectoryStream and exposes the underlying java.nio.file.DirectoryStream as a java.util.stream.Stream, so their functionality is basically the same except that Files.newDirectoryStream allows you to pass an optional DirectoryStream.Filter.

Files.walkFileTree additionally exposes BasicFileAttributes (such as lastModifiedTime, isRegularFile, and size). If you are going to need these attributes, it could be more convenient (and possibly more efficient) to get them from Files.walkFileTree instead of looking them up separately.

Keever answered 16/2, 2021 at 18:24 Comment(0)
C
2

All these 3 solutions look correct, but it's better to use the simplest and the most readable way, so Files.list() looks natural to solve this problem.

Cuba answered 16/6, 2016 at 12:26 Comment(0)
T
0

Keep in mind that when using

Files.list(directory)
    .forEach(path -> {
        try {
            doSomething(path);
        } catch (IOException exc) {
            // log exc
        }
    });

or

Files.walk(directory, 1)
    .forEach(path -> {
        try {
            doSomething(path);
        } catch (IOException exc) {
            // log exc
        }
});

you MUST use try-with-resources if you don't want to introduce resource leak as stated in Files.list() documentation:

The returned stream encapsulates a DirectoryStream. If timely disposal of file system resources is required, the try-with-resources construct should be used to ensure that the stream's close method is invoked after the stream operations are completed.

so the best option should look something like:

try(Stream<Path> list = Files.list(directory)) {
    list.forEach(path -> {
        try {
            doSomething(path);
        } catch (IOException exc) {
            // log exc
        }
    });
}
Tannertannery answered 20/11, 2023 at 13:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.