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.

Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

Leave a Reply


two × = 8



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books