Sunday 12 February 2017

Walking a directory using Streams API in java8

Background

In one of the previous posts we saw how we can traverse a directory in Java as part of NIO.2 APIs introduced in Java 8 using walkFileTree method and Path and FileVisitor arguments.
 In this post we will try to do the same thing but with stream APIs introduced in Java8.

Walking a directory using Streams API

In this we will use Files.walk(path) method that returns a Stream<Path> instance and traverse the directories in dept first pattern. This is lazy implementation which mean child directories are not actually loaded until it's parent is traversed.

In previous post we say how we can filter .java files using PathMatcher interface. In this post we will see how easy it is with Streams.

        Path path = Paths.get("/Users/athakur/Desktop/testDir");
        
        try {
            Files.walk(path)
            .filter(p -> p.toString().endsWith(".java"))
            .forEach(System.out::println);
            } catch (IOException e) {
                e.printStackTrace();
            }

and the output is : 
/Users/athakur/Desktop/testDir/innerTestDir1/test2.java
/Users/athakur/Desktop/testDir/innerTestDir1/test3.java
/Users/athakur/Desktop/testDir/innerTestDir2/test4.java
/Users/athakur/Desktop/testDir/test1.java

File structure is as follows -
  • testDir
    • test1.java
    • innerTestDir1
      • test2.java
      • test3.java
    • innerTestDir2
      • test4.java
As you can see it does a depth first search.

NOTE : By default the directories depth searched is Integer.MAX_VALUE. Keeping it default and having a deep/large directory structure may take a lot of time to search. So the walk method has an overloaded method that takes in integer parameter (walk(Path,int)) denoting dept of directories to be searched.

NOTE : Unlike earlier NIO.2 methods walk by default will not traverse symbolic links. This is to avoid traversing unnecessary paths or cyclic paths. But if you do wish to track symbolic links you can provide FOLLOW_LINKS option as a vararg to the walk() method. However you should provide a depth is this case to avoid unnecessary traversals. Also walk method keeps track of path traversed and if t detects a loop it will throws FileSystemLoopException.

Files class has other helpful methods as well for searching, listing and printing files -

        try {

            System.out.println("Printing Java regular files");
            Files.find(path, 10, (p, a) -> p.toString().endsWith(".java") && a.isRegularFile())
                    .forEach(System.out::println);
            System.out.println("Printing non directories files");
            // traverses only 1 depth
            Files.list(path).filter(p -> !Files.isDirectory(p)).map(p -> p.toAbsolutePath())
                    .forEach(System.out::println);

        } catch (IOException e) {
            e.printStackTrace();
        }
       


Output :
Printing Java regular files
/Users/athakur/Desktop/testDir/innerTestDir1/test2.java
/Users/athakur/Desktop/testDir/innerTestDir1/test3.java
/Users/athakur/Desktop/testDir/innerTestDir2/test4.java
/Users/athakur/Desktop/testDir/test1.java
Printing non directories files
/Users/athakur/Desktop/testDir/test1.java

NOTE : Notice how Files.list() traverses only 1 depth unlike Files.find() which traverses till the depth provided. Also notice how each method throws IOException since file may not actually be present.

Following picture shows difference between legacy file APIs and NIO2 APIs -




Related Links

No comments:

Post a Comment

t> UA-39527780-1 back to top