Java
Programming
File Manipulation
Recursion
Coding Techniques

Recursively list files in Java

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Recursively listing files means walking a directory tree and visiting every nested file under a starting path. In Java, the modern solution is almost always in java.nio.file, not java.io.File, because the newer API gives you better error handling, symbolic-link control, and clearer traversal logic. The older recursive File pattern still works, but it is usually a compatibility choice rather than the best one for new code.

The simplest modern approach is Files.walk

If you just want every file path under a directory, Files.walk is the shortest readable solution. It returns a stream of paths that already includes recursive traversal.

java
1import java.io.IOException;
2import java.nio.file.Files;
3import java.nio.file.Path;
4import java.nio.file.Paths;
5
6public class WalkExample {
7    public static void main(String[] args) throws IOException {
8        Path root = Paths.get("src");
9
10        try (var paths = Files.walk(root)) {
11            paths.filter(Files::isRegularFile)
12                 .forEach(System.out::println);
13        }
14    }
15}

This is a good default for small utilities and reporting tasks. The try block is important because the stream holds filesystem resources that should be closed promptly.

Use walkFileTree when you need control

Files.walk is concise, but Files.walkFileTree is the better tool when you need custom behavior for errors, symbolic links, or per-directory hooks. It uses the visitor pattern, which is more verbose but more explicit.

java
1import java.io.IOException;
2import java.nio.file.FileVisitResult;
3import java.nio.file.Files;
4import java.nio.file.Path;
5import java.nio.file.Paths;
6import java.nio.file.SimpleFileVisitor;
7import java.nio.file.attribute.BasicFileAttributes;
8
9public class WalkFileTreeExample {
10    public static void main(String[] args) throws IOException {
11        Path root = Paths.get("src");
12
13        Files.walkFileTree(root, new SimpleFileVisitor<>() {
14            @Override
15            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
16                if (attrs.isRegularFile()) {
17                    System.out.println(file.toAbsolutePath());
18                }
19                return FileVisitResult.CONTINUE;
20            }
21
22            @Override
23            public FileVisitResult visitFileFailed(Path file, IOException exc) {
24                System.err.println("Skipping " + file + ": " + exc.getMessage());
25                return FileVisitResult.CONTINUE;
26            }
27        });
28    }
29}

This style is especially useful for production code because you can decide how to handle unreadable directories instead of failing the whole traversal.

The older File recursion still works

If you are working in a legacy codebase, you may still see a direct recursive method based on java.io.File.

java
1import java.io.File;
2
3public class LegacyRecursiveList {
4    public static void listFiles(File directory) {
5        File[] entries = directory.listFiles();
6        if (entries == null) {
7            return;
8        }
9
10        for (File entry : entries) {
11            if (entry.isDirectory()) {
12                listFiles(entry);
13            } else if (entry.isFile()) {
14                System.out.println(entry.getAbsolutePath());
15            }
16        }
17    }
18
19    public static void main(String[] args) {
20        listFiles(new File("src"));
21    }
22}

It is easy to understand, but it gives you less control, less metadata, and weaker error reporting than the NIO API. For new code, Path and Files are usually the better choice.

Filter while traversing

Most real programs do not want every file. They want Java source files, image files, or files newer than a certain date. Both Files.walk and walkFileTree let you apply that filter naturally.

java
1import java.io.IOException;
2import java.nio.file.Files;
3import java.nio.file.Path;
4import java.nio.file.Paths;
5
6public class JavaSourceOnly {
7    public static void main(String[] args) throws IOException {
8        Path root = Paths.get("src");
9
10        try (var paths = Files.walk(root)) {
11            paths.filter(Files::isRegularFile)
12                 .filter(path -> path.toString().endsWith(".java"))
13                 .forEach(System.out::println);
14        }
15    }
16}

Keeping the filter close to the traversal makes the intent obvious and avoids collecting unnecessary paths into memory.

Common Pitfalls

One common mistake is forgetting to close the stream returned by Files.walk. Use a try block so directory handles are released properly.

Another issue is following symbolic links without thinking through cycles. If your directory tree can contain links back into earlier folders, you need to decide whether link-following is safe.

Developers also assume listFiles() always returns an array. It can return null when the directory is unreadable or invalid, so legacy recursion code must check for that.

Finally, avoid loading every path into a list unless you really need all of them at once. Streaming the traversal is usually more memory-friendly.

Summary

  • For new Java code, prefer Files.walk or Files.walkFileTree over java.io.File.
  • 'Files.walk is concise and works well for straightforward recursive listing.'
  • 'walkFileTree is better when you need custom error handling or traversal rules.'
  • Use filters during traversal so you only process the files you actually need.
  • Close streams and think about permissions and symbolic links in real-world code.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.