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
- Filtering using
newDirectoryStream (Path dir, DirectoryStream.Filterfilter)
- Filtering using
DirectoryStream.Filter
- Filtering using
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.
Reference: | Listing and filtering directory contents in NIO.2 from our JCG partner Jakub Stas at the Jakub Stas blog. |