Directory traversal with Files.list and Files.walk
Listing directory contents and walking file trees — both return a Stream that must be closed.
Walking the file tree
Files.list(dir) returns a Stream<Path> of the direct children of a directory (not recursive):
try (Stream<Path> entries = Files.list(Path.of("src"))) {
entries.filter(Files::isRegularFile)
.forEach(System.out::println);
}
Files.walk(dir) walks recursively — every file and directory in the subtree:
try (Stream<Path> all = Files.walk(Path.of("."), 3)) { // max depth 3
all.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}
The second argument is the max depth (optional — omit it to walk the entire tree).
Closing is mandatory. Both Files.list and Files.walk hold open a directory handle. Forgetting to close leaks the handle. Always use try-with-resources.
Files.newDirectoryStream is an alternative that produces an Iterable<Path> instead of a stream — useful in a for-each loop:
try (DirectoryStream<Path> ds =
Files.newDirectoryStream(Path.of("."), "*.{java,class}")) {
for (Path p : ds) System.out.println(p);
}
The glob pattern "*.{java,class}" uses PathMatcher syntax — simpler than a predicate when you just need to filter by extension.