| Lesson 7 | Modern Filename Filters Using Java NIO |
| Objective | Learn to filter and display specific file types using Java NIO’s DirectoryStream and PathMatcher APIs. |
In legacy Java I/O, filtering files relied on the java.io.FilenameFilter interface, which determined whether a given file should be displayed in a directory listing or file dialog box. While functional, that approach is now largely replaced by the Java NIO (New Input/Output) APIs, introduced in Java 1.4 and significantly enhanced in Java 7 (NIO.2). These APIs offer non-blocking I/O, better scalability, and fine-grained control over file operations using Path, DirectoryStream, and Files.
The traditional java.io.File API suffers from several limitations:
false instead of throwing exceptions).In contrast, java.nio.file introduces a modern, efficient, and extensible way to filter, walk, and monitor files using:
The DirectoryStream API allows developers to filter files using a lambda or anonymous function. The following example lists only .java, .html, and .txt files in a given directory.
import java.io.IOException;
import java.nio.file.*;
import java.util.Arrays;
import java.util.List;
public class NioFilenameFilterExample {
public static void main(String[] args) {
Path dir = Paths.get(System.getProperty("user.home"));
List<String> allowedExtensions = Arrays.asList(".java", ".html", ".txt");
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, entry -> {
String name = entry.getFileName().toString().toLowerCase();
return allowedExtensions.stream().anyMatch(name::endsWith);
})) {
System.out.println("Filtered files in directory: " + dir);
for (Path entry : stream) {
System.out.println(" → " + entry.getFileName());
}
} catch (IOException e) {
System.err.println("Error reading directory: " + e.getMessage());
}
}
}
Here, the DirectoryStream.Filter<Path> interface replaces FilenameFilter. The filtering logic is expressed in a concise lambda expression, making it both readable and maintainable.
The PathMatcher interface provides powerful pattern-matching capabilities using either glob or regex syntax. This approach is ideal for complex filtering requirements.
import java.io.IOException;
import java.nio.file.*;
public class PathMatcherExample {
public static void main(String[] args) throws IOException {
Path dir = Paths.get(System.getProperty("user.dir"));
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**/*.{java,html,txt}");
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
System.out.println("Files matching glob pattern (*.java, *.html, *.txt):");
for (Path entry : stream) {
if (matcher.matches(entry)) {
System.out.println(" → " + entry.getFileName());
}
}
}
}
}
Using glob: syntax allows you to express patterns similar to shell wildcards, while regex: provides full regular expression support for complex use cases.
To scan directories recursively while applying filters, the Files.walk() method can be combined with the Stream API:
import java.io.IOException;
import java.nio.file.*;
import java.util.stream.Stream;
public class RecursiveFileFilter {
public static void main(String[] args) {
Path startDir = Paths.get(System.getProperty("user.home"));
try (Stream<Path> stream = Files.walk(startDir)) {
stream.filter(p -> Files.isRegularFile(p))
.filter(p -> p.toString().endsWith(".java")
|| p.toString().endsWith(".html")
|| p.toString().endsWith(".txt"))
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
This approach is non-blocking and supports parallel execution when combined with parallel(), making it ideal for large directory structures or file processing pipelines.
Modern JavaFX or Swing file choosers can easily integrate with NIO-based filtering. For instance, in JavaFX:
import javafx.stage.FileChooser;
import javafx.application.Application;
import javafx.stage.Stage;
public class JavaFXFileChooserWithFilter extends Application {
@Override
public void start(Stage stage) {
FileChooser chooser = new FileChooser();
chooser.setTitle("Select a File");
chooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("Source Files", "*.java"),
new FileChooser.ExtensionFilter("HTML Files", "*.html"),
new FileChooser.ExtensionFilter("Text Files", "*.txt")
);
chooser.showOpenDialog(stage);
}
public static void main(String[] args) {
launch();
}
}
Using ExtensionFilter in JavaFX allows the UI to present the same filtering logic as NIO but in a user-friendly interface. This creates a seamless workflow between backend I/O operations and the frontend user experience.
The legacy FilenameFilter served its purpose in early versions of Java, but modern applications benefit from the flexibility, scalability, and readability of the NIO-based approach. Whether filtering directories with DirectoryStream, matching filenames via PathMatcher, or integrating filtering logic with file choosers, Java’s modern I/O API provides a clean, consistent way to manage files across diverse platforms and workloads.
In the next module, we’ll explore how Java NIO Channels and Buffers provide efficient file transfer and memory-mapped I/O for large data sets.
File class, file dialog boxes, and filename filters.