高效查找特定目录下的文件
Efficently find files in specific directories
我有一个简单的问题:我使用 Files.walkFileTree
迭代一个大而深嵌套的目录结构,如下所示:
final int CUTOFF = 5;
final List<Path> foundList = new ArrayList<>();
Files.walkFileTree( codeRoot, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
String rPath = codeRoot.relativize( dir ).toString();
int level = rPath.length() - rPath.replace("/", "").length();
if (dir.getFileName().toString().equals( "target" ) || level < CUTOFF) {
return FileVisitResult.CONTINUE;
}
return FileVisitResult.SKIP_SUBTREE;
}
@Override
public FileVisitResult visitFile( Path file, BasicFileAttributes attrs )
throws IOException {
if (file.getFileName().toString().endsWith( ".txt" )) {
foundList.add( file );
}
return FileVisitResult.CONTINUE;
}
} );
我的目标是添加特定目录 target
下的所有文件,我知道在 codeRoot
.
下最多 CUTOFF
层
我正在寻找一种更有效的方法来根据必要的 stat()
调用或有人说 "can't be done"。
语言水平 Java8。
优化选项:
1) 目录更改时注册通知:https://docs.oracle.com/javase/tutorial/essential/io/notification.html
这可以在后台工作
2)(不太理想)使用未更改目录的缓存(在某些文件系统中):使用目录的最后修改时间来缓存自上次调用后未更改的目录
使用 grepcode,我找不到 relativize 是如何实现的,我认为它可能是本地实现的。我想它是通过对已经提取的值进行简单的字符串操作来实现的,我认为它根本不会访问 stat()
。不过,您可以对其进行测试,在有和没有 relativize
的情况下制作一个虚拟代码(没有任何用处),并在遍历大量文件时测量实际影响。你可以确定你不会因为 relativize
而损失太多性能
提出的算法是一次性查询。在这种情况下,您将在所有目录中进行线性时间搜索。您不能最大限度地减少以这种方式检查每个目录的需要。当然,您可以查看缓存,但是如果您要为缓存一致性而烦恼并且需要高性能,您也可以考虑构建索引。无论哪种情况,我都会解决您提出的问题,这是关于一次性查询的。
您正在使用的 Files.walkFileTree
版本遍历整个树,包括超过最大级别的所有文件和目录。您通过解析路径名明确排除了它们,您认为这种技术可能效率不高。解决方案是始终阅读文档。 Files.walkFileTree
的第二个版本将最大深度作为显式参数。来自 tutorial on walking the file tree:
The second walkFileTree method enables you to additionally specify a limit on the number of levels visited and a set of FileVisitOption enums.
如果你使用第二种方法,你将只访问最大级别内的候选文件,你可以避免所有剪枝子树的代码。
我有一个简单的问题:我使用 Files.walkFileTree
迭代一个大而深嵌套的目录结构,如下所示:
final int CUTOFF = 5;
final List<Path> foundList = new ArrayList<>();
Files.walkFileTree( codeRoot, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
String rPath = codeRoot.relativize( dir ).toString();
int level = rPath.length() - rPath.replace("/", "").length();
if (dir.getFileName().toString().equals( "target" ) || level < CUTOFF) {
return FileVisitResult.CONTINUE;
}
return FileVisitResult.SKIP_SUBTREE;
}
@Override
public FileVisitResult visitFile( Path file, BasicFileAttributes attrs )
throws IOException {
if (file.getFileName().toString().endsWith( ".txt" )) {
foundList.add( file );
}
return FileVisitResult.CONTINUE;
}
} );
我的目标是添加特定目录 target
下的所有文件,我知道在 codeRoot
.
CUTOFF
层
我正在寻找一种更有效的方法来根据必要的 stat()
调用或有人说 "can't be done"。
语言水平 Java8。
优化选项:
1) 目录更改时注册通知:https://docs.oracle.com/javase/tutorial/essential/io/notification.html 这可以在后台工作
2)(不太理想)使用未更改目录的缓存(在某些文件系统中):使用目录的最后修改时间来缓存自上次调用后未更改的目录
使用 grepcode,我找不到 relativize 是如何实现的,我认为它可能是本地实现的。我想它是通过对已经提取的值进行简单的字符串操作来实现的,我认为它根本不会访问 stat()
。不过,您可以对其进行测试,在有和没有 relativize
的情况下制作一个虚拟代码(没有任何用处),并在遍历大量文件时测量实际影响。你可以确定你不会因为 relativize
提出的算法是一次性查询。在这种情况下,您将在所有目录中进行线性时间搜索。您不能最大限度地减少以这种方式检查每个目录的需要。当然,您可以查看缓存,但是如果您要为缓存一致性而烦恼并且需要高性能,您也可以考虑构建索引。无论哪种情况,我都会解决您提出的问题,这是关于一次性查询的。
您正在使用的 Files.walkFileTree
版本遍历整个树,包括超过最大级别的所有文件和目录。您通过解析路径名明确排除了它们,您认为这种技术可能效率不高。解决方案是始终阅读文档。 Files.walkFileTree
的第二个版本将最大深度作为显式参数。来自 tutorial on walking the file tree:
The second walkFileTree method enables you to additionally specify a limit on the number of levels visited and a set of FileVisitOption enums.
如果你使用第二种方法,你将只访问最大级别内的候选文件,你可以避免所有剪枝子树的代码。