Core Java

Listing and filtering directory contents in NIO.2

There hasn’t been much happening in the area of listing directory contents until the release of Java 7. But since NIO.2 introduced a new way to do this it might be worth it to cover this area. One of big pluses of NIO.2 is the ability to use listing and filtering at once in one method call. This provides an elegant solution to most of listing/filtering needs related to work with a file system.

Listing root directories

Unless we are working with relative paths we need to be aware of the environment where our application lives, so we can define absolute paths. Since file systems are usually hierarchical structures there is at least one root directory. To properly address files and directories we need to be able to list all these root directories. To do this, we turn to the FileSystem instance itself to use its method getRootDirectories, which is an alternative to Java 6 construct File.listRoots().

Iterable<Path> it = FileSystems.getDefault().getRootDirectories();

System.out.println("Root file system locations: " + Sets.newHashSet(it));

*Please note that class Sets is not part of JDK, but comes from Google’s Guava library. I used it here, just for convenience to get nicely formated string representation of root directories.

With following output:

Root file system locations: C:\, D:\, E:\, F:\, G:\, H:\, I:\,

Listing and filtering directory contents

Standard task when working with file system is to list or filter files within given directory. We might need to modify, analyze or simply list them – whatever the reason, class java.nio.file.Files has our backs. It offers three variants of method newDirectoryStream that return object of type DirectoryStream<Path> to allow us to iterate over the entries in a directory. Here we see an apparent difference between current and prior versions of IO library (returning simple arrays) preventing NullPointerException. Following example shows how simple it is to list contents of given directory:

Path directoryPath = Paths.get("C:", "Program Files/Java/jdk1.7.0_40/src/java/nio/file");

if (Files.isDirectory(directoryPath)) {
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(directoryPath)) {
        for (Path path : stream) {
            System.out.println(path);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

Please notice the use of isDirectory checking method that prevents NotDirectoryException. Also note the use of the try-with-resources construct – DirectoryStream is both AutoCloseable and Closeable (meaning it needs to be closed at some time) so try-with-resources comes in handy. Code returns following output:

...
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\CopyOption.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\DirectoryIteratorException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\DirectoryNotEmptyException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\DirectoryStream.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileAlreadyExistsException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\Files.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileStore.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystem.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystemAlreadyExistsException.java
...

To ensure universal usability of DirectoryStream<Path> we can filter using two basic mechanisms:

  • newDirectoryStream(Path dir, String glob)
    • Filtering using GLOB
  • newDirectoryStream (Path dir, DirectoryStream.Filterfilter)
    • Filtering using  DirectoryStream.Filter

Filtering with GLOB pattern

First of all we need to know what a GLOB is. GLOB patterns are string expressions that follow specific syntax rules and they are used for matching purposes. Please refer to the following article for more information on GLOBs and GLOB syntax. When it comes to filtering using GLOBs, Files class provides us with an easy way to do so. Lets take a look at following example.

Path directoryPath = Paths.get("C:", "Program Files/Java/jdk1.7.0_40/src/java/nio/file");

if (Files.isDirectory(directoryPath)) {
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(directoryPath, "File*Exception*")) {
        for (Path path : stream) {
            System.out.println(path);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

With following output:

C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileAlreadyExistsException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystemAlreadyExistsException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystemException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystemLoopException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystemNotFoundException.java

Filtering with DirectoryStream.Filter

When the task at hand requires more complex filtering options rather than just simple file name matching, we need to implement interface DirectoryStream.Filter<Path>. This is the most powerful filtering option available at our disposal since we have an access to the rest of the application and might use third party libraries. Following example shows such a situation with two filtering conditions:

  • File size must be an even number
  • Time of execution in milliseconds must be an even number
Path directoryPath = Paths.get("C:", "Program Files/Java/jdk1.7.0_40/src/java/nio/file");
DirectoryStream.Filter<Path> filter = new Filter<Path>() {

    @Override
    public boolean accept(Path entry) throws IOException {
        long size = Files.readAttributes(entry, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).size();
        long milis = new Date().getTime();

        boolean isSizeEvenNumber = size % 2 == 0;
        boolean isTheTimeRight = milis % 2 == 0;

        return isTheTimeRight && isSizeEvenNumber;
    }
};

if (Files.isDirectory(directoryPath)) {
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(directoryPath, filter)) {
        for (Path path : stream) {
            System.out.println(path);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

With following output:

C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\DirectoryStream.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileAlreadyExistsException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\Files.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\NotDirectoryException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\NotLinkException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\package-info.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\WatchEvent.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\WatchService.java

*Please note that based on used conditions filtered files may differ per execution.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button