2015-09-30 34 views
6

我有一个简单的问题Efficently查找文件:我重复使用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()来电或有人说“不能做”的术语来做到这一点。

语言水平Java8。

+0

你为什么认为这可以做到? walkFileTree使用NIO,这意味着它在性能方面很少像本地散步一样好。如果你经常调用它,你可以使用一些缓存。缓存示例:目录(在某些文件系统中)的最后修改时间,用于缓存自上次调用以来未更改过的目录。 –

+0

@MaddenAdamovic我主要想着我可能会错过一些算法快捷方式,因为我的实现过程很天真。另外,我不知道'relativize()'对我可以避免的fs性能有什么影响。你对重复运行优化的想法很好,谢谢! – mabi

+0

你用什么来衡量速度?你是否已经在C/C++中实现了一个类似的解决方案作为参考点?你为什么认为目前效率低下? – Fallso

回答

1

提出的算法是一次性查询。在这种情况下,你坚持在所有目录中进行线性时间搜索。您不能最小化以这种方式检查每个目录的需要。当然,您可以查看缓存,但如果您打算使用缓存一致性并需要高性能,则可能还需要考虑构建索引。无论哪种情况,我都会回答您提出的问题,即一次性查询。

的您正在使用Files.walkFileTree版本遍历整个树,包括所有的文件和目录过去的最高水平。你明确地通过解析路径名来排​​除它们,这是一种你认为效率不高的技术。解决方案是始终阅读文档。有一个Files.walkFileTree的第二个版本,最大深度作为明确的参数。从tutorial on walking the file tree

第二个walkFileTree方法使您可以额外指定访问级别数量和一组FileVisitOption枚举的限制。

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

+0

好的方法。这促使我看看'walkFileTree'实现,它使用'stack'来跟踪要访问的目录。 “SKIP_SUBTREE”的返回将弹出堆栈元素,该元素应该*结束遍历(通过不为该目录生成新的堆栈条目),是否正确?所以你说这两个是相同的,但使用'maxDepth'变体,我可以减少手动深度计算? – mabi

+0

@mabi'SKIP_SUBTREE'的操作通常被称为“修剪”。它在当前节点停止遍历,避免遍历其所有子节点,并且继续_as if子树被遍历。所以,是的,你对这种行为的分析是正确的。对于第二个问题,使用'maxDepth'的实现确实跟踪了深度(事实上,它已经这样做了,因为它是堆栈的大小),从而减轻了你计算它的需要。提示:不要编写其他人已经为您编写的代码。 – eh9

+0

公平点。既然你已经击中了“你不能做更多”和“有优化空间”的点,我会奖励你赏金EOD,除非在此之前有人打动我的脑海。 – mabi

1

优化选项:

1)报名通知时,目录的变化:https://docs.oracle.com/javase/tutorial/essential/io/notification.html 这可以在后台

2)(非最佳)工作使用没有改变目录的高速缓存(在一些文件系统):使用目录的上次修改时间来缓存自上次调用以来未更改的目录

使用grepcode时,我无法找到如何实现相对化,我认为它可能是本机实现的。我想这是用简单的字符串操作实现的已经拉动的值,我不认为它正在访问stat()。你可以测试它,尽管在有或没有relativize的情况下编写一个虚拟代码(这不起作用),并在遍历大量文件时测量实际影响。比你能确保你不会损失太多的性能,因为relativize

+0

'relativize()'依赖于JVM + OS,在我的情况下,它是通过'sun.nio.fs.UnixPath'实现的。反编译的代码有点难以追踪。 – mabi

+0

创建一个测试代码(无需执行任何操作即可遍历目录),并且可以执行相关性和测试性能。如果你获得的性能相对减少30%,你应该试着弄清楚如何解决这个问题 –