SyntaxStudy
Sign Up
Java Walking Directory Trees with Files.walk
Java Beginner 1 min read

Walking Directory Trees with Files.walk

Files.walk() returns a Stream that lazily walks a directory tree in depth-first order. The optional second parameter maxDepth limits how deep the traversal goes — passing 1 gives you only the immediate children of the directory. Combined with stream operations like filter and map, Files.walk() provides an elegant way to find, process, or delete files matching certain criteria without external libraries. Files.walkFileTree() is the lower-level visitor-based API for traversing directory trees. You implement the FileVisitor interface (or extend SimpleFileVisitor) and override methods that are called at each step: visitFile() when a file is visited, preVisitDirectory() before entering a directory, postVisitDirectory() after leaving one, and visitFileFailed() on errors. This gives you fine-grained control, such as the ability to skip subtrees or delete a directory and all its contents in the correct order. Files.find() is a convenience combination of Files.walk() and a filter: it takes a directory, max depth, and a BiPredicate and returns a Stream of matching paths. It avoids calling Files.readAttributes() separately for each path since the attributes are already available during the walk, making it more efficient than walking and then querying attributes.
Example
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.stream.Collectors;

public class DirectoryWalkDemo {

    public static void main(String[] args) throws IOException {
        // Set up a demo directory tree
        Path root = Path.of("demo-tree");
        Files.createDirectories(root.resolve("src/main"));
        Files.createDirectories(root.resolve("src/test"));
        Files.writeString(root.resolve("src/main/App.java"),     "// App");
        Files.writeString(root.resolve("src/main/Util.java"),    "// Util");
        Files.writeString(root.resolve("src/test/AppTest.java"), "// Test");
        Files.writeString(root.resolve("README.md"),             "# Demo");

        // --- Files.walk: all .java files ---
        System.out.println("All .java files:");
        try (var stream = Files.walk(root)) {
            stream.filter(p -> p.toString().endsWith(".java"))
                  .forEach(p -> System.out.println("  " + p));
        }

        // --- Files.walk: count files vs directories ---
        try (var stream = Files.walk(root)) {
            long fileCount = stream.filter(Files::isRegularFile).count();
            System.out.println("Regular file count: " + fileCount);
        }

        // --- Files.find with attributes ---
        System.out.println("\nFiles larger than 5 bytes:");
        try (var stream = Files.find(root, 10,
                (path, attrs) -> attrs.isRegularFile() && attrs.size() > 5)) {
            stream.forEach(p -> System.out.println("  " + p));
        }

        // --- SimpleFileVisitor: delete tree ---
        Files.walkFileTree(root, new SimpleFileVisitor<>() {
            @Override public FileVisitResult visitFile(Path f, BasicFileAttributes a)
                    throws IOException { Files.delete(f); return FileVisitResult.CONTINUE; }
            @Override public FileVisitResult postVisitDirectory(Path d, IOException e)
                    throws IOException { Files.delete(d); return FileVisitResult.CONTINUE; }
        });
        System.out.println("Tree deleted. Exists: " + Files.exists(root));
    }
}