英文:
How to list directories only if there is no further subdirectory present using NIO in Java 8?
问题
使用Java 8 NIO和以下代码来列出目录:
String rootDir = "root";
Files.walk(Paths.get(rootDir))
.filter(Files::isDirectory)
.forEach(folder_path -> {
System.out.println(folder_path.toString());
});
但这将会产生以下输出:
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
但是,如果只有没有进一步的子目录时才显示路径,你可以使用以下代码来实现预期的输出:
String rootDir = "root";
Files.walk(Paths.get(rootDir))
.filter(path -> Files.isDirectory(path) && Files.list(path).noneMatch(Files::isDirectory))
.forEach(folder_path -> {
System.out.println(folder_path.toString());
});
这将产生以下输出:
root\dir1\subdir1
root\dir1\subdir2
root\dir2\subdir1
root\dir2\subdir2
root\dir3\subdir1
root\dir3\subdir2
这个代码会检查每个目录,仅当该目录没有子目录时才输出路径。
英文:
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?
答案1
得分: 3
你可以直接实现一个过滤器,例如:
```java
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);
这个方法简单但在每个遇到的目录上执行了冗余操作。
一个更有效的替代方法是经典的循环解决方案:
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);
它在遇到另一个目录时简单地删除父目录,因此在此单次迭代结束时,只剩下不包含其他目录的目录。
或者使用老式的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;
}
});
通过重写 postVisitDirectory
(而不是 preVisitDirectory
),我们在访问其子目录之前访问了一个子目录,如果有的话。因此,仅需要简单地测试前一个路径是否是当前路径的子目录。
<details>
<summary>英文:</summary>
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.
</details>
# 答案2
**得分**: 0
你可以创建一个用于检查子目录并将其添加到过滤器中的方法,如下所示:
```java
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);
英文:
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);
答案3
得分: 0
Another simple version which uses streams:
LinkedHashSet
try (Stream
stream.filter(dirs::add).map(Path::getParent).forEach(dirs::remove);
}
英文:
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);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论