Explain Codes LogoExplain Codes Logo

How do I iterate through the files in a directory and its sub-directories in Java?

java
directory-traversal
file-system
java-8
Alex KataevbyAlex Kataev·Oct 27, 2024
TLDR

Files.walk in Java succinctly traverses directories. Try this:

try (Stream<Path> files = Files.walk(Path.of("dir"))) { files.forEach(System.out::println); }

This piece of code streams every path, printing each file and directory recursively starting from "dir".

Using Files.walk, keep in mind circular symbolic links could result in infinite recursion. To limit this, sparingly use FileVisitOption.FOLLOW_LINKS and enforce a maxDepth:

Files.walk(Path.of("dir"), maxDepth, FileVisitOption.FOLLOW_LINKS);

Custom directory traversal with Files.walkFileTree

For maximum control and flexibility, pair FileVisitor with Files.walkFileTree for additional behavior:

Files.walkFileTree(Path.of("dir"), new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { System.out.println(file); // Hello, file! return FileVisitResult.CONTINUE; } // Override other methods for more fun });

This code opens the door for robust traversal logic within hooks for each file visit.

Utilizing Apache Commons IO

For a higher level of abstraction, turn to Apache Commons IO and its FileUtils.listFiles:

Collection<File> files = FileUtils.listFiles( new File("dir"), TrueFileFilter.INSTANCE, DirectoryFileFilter.DIRECTORY ); files.forEach(System.out::println); // Printing party!

The Apache Commons IO library delivers clean, reliable directory traversal, while ensuring your hands stay clean from boilerplate code.

Sifting through files with Files.newDirectoryStream

With Java 7+ and Files.newDirectoryStream, you can join file filtering with directory iteration:

try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.java")) { for (Path entry : stream) { System.out.println(entry); // File, is that you? } } catch (IOException | DirectoryIteratorException ex) { // Oops, handle the error }

This approach not only efficiently uses the DirectoryStream, but also neatly filters files by type.

Tackling exceptions in DirectoryStream

Unruly DirectoryIteratorExceptions may pop up during your stream operations. Keep calm and handle it:

try (Stream<Path> stream = Files.walk(Path.of("dir"))) { stream.forEach(p -> { try { System.out.println(p); // Surprise, a file! } catch (Exception e) { throw new DirectoryIteratorException(new IOException(e)); } }); } catch (DirectoryIteratorException ex) { // I've got it under control }

By neatly wrapping this exception, we maintain clear, good practice error management.

Choosing Path over File

In Java 8, it's recommended to forgo File in favor of Path when traversing directories:

Path start = Paths.get("dir"); Files.walk(start) .filter(Files::isRegularFile) .forEach(System.out::println);

This modernizes your code and aligns it with Java's recommended practices for I/O operations.

Collecting your results

During iteration, you might want to gather all paths:

List<Path> gatheredPaths = Files.walk(Path.of("dir")) .collect(Collectors.toList());

With a List<Path>, you can venture further and perform additional operations, like sorting or further filtering.