高效查找特定目录下的文件

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.

如果你使用第二种方法,你将只访问最大级别内的候选文件,你可以避免所有剪枝子树的代码。