Reputation: 21396
I am using Java 8 NIO and the below code to list directories;
String rootDir = "root\\";
Files.walk(Paths.get(rootDir)).filter(Files::isDirectory).forEach(folder_path -> {
System.out.println(folder_path.toString());
});
But this will give the output like below;
root
root\dir1
root\dir1\subdir1
root\dir1\subdir2
root\dir2
root\dir2\subdir1
root\dir2\subdir2
root\dir3
root\dir3\subdir1
root\dir3\subdir2
But, I would like to display the path, only if there is no further sub-directories. So, the expected output will be like;
root\dir1\subdir1
root\dir1\subdir2
root\dir2\subdir1
root\dir2\subdir2
root\dir3\subdir1
root\dir3\subdir2
Is this possible? If yes, how can I achieve this?
Upvotes: 1
Views: 106
Reputation: 15136
Another simple version which uses streams:
LinkedHashSet<Path> dirs = new LinkedHashSet<>();
try(Stream<Path> stream = Files.find(dir, Integer.MAX_VALUE, (path,attr) -> attr.isDirectory())) {
stream.filter(dirs::add).map(Path::getParent).forEach(dirs::remove);
}
Upvotes: 0
Reputation: 298153
You can implement a filter straight-forwardly, e.g.
Files.walk(Paths.get(rootDir))
.filter(Files::isDirectory)
.filter(dir -> {
try(Stream<Path> sub=Files.list(dir)) { return sub.noneMatch(Files::isDirectory); }
catch(IOException ex) { throw new UncheckedIOException(ex); }
})
.forEach(System.out::println);
which is simple but performs redundant operations on each encountered directory.
A more efficient alternative would be a classic loop solution:
Set<Path> dirs = new LinkedHashSet<>();
for(Iterator<Path> it = Files.walk(Paths.get(rootDir)).iterator(); it.hasNext(); ) {
Path p = it.next();
if(Files.isDirectory(p) && dirs.add(p)) dirs.remove(p.getParent());
}
dirs.forEach(System.out::println);
It simply removes parent directories when encountering another directory, so at the end of this single iteration, only directories not containing other directories are left.
Or with the good old Java 7 API:
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
Path previous;
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
if(previous == null || !previous.startsWith(dir))
System.out.println(dir);
previous = dir;
return FileVisitResult.CONTINUE;
}
});
By overriding postVisitDirectory
(instead preVisitDirectory
), we have visited a child directory of it right before, if there was one. So a simple test whether the previous path is a child of the current is sufficient.
Upvotes: 3
Reputation: 7165
You can create a method to check sub-directory and add it in filter as below,
private static Predicate<Path> checkSubDirectory() {
return f1->{
for (File file : f1.toFile().listFiles()) {
if (file.isDirectory())
return false;
}
return true;
};
}
Files.walk(Paths.get(rootDir)).filter(Files::isDirectory)
.filter(checkSubDirectory())
.forEach(System.out::println);
Upvotes: 0