Files.walk.filter 和 Files.find 有啥区别?

Posted

技术标签:

【中文标题】Files.walk.filter 和 Files.find 有啥区别?【英文标题】:What is the difference between Files.walk.filter and Files.find?Files.walk.filter 和 Files.find 有什么区别? 【发布时间】:2017-07-04 08:50:17 【问题描述】:

此代码搜索特定文件:

Stream<Path> findMyFile = Files.find(Paths.get("c:\\temp\\pathtest"), Integer.MAX_VALUE,(p, a) -> p.endsWith("test.txt") && a.isRegularFile());

Stream<Path> findMyFileSecond = Files.walk(Paths.get("c:\\temp\\pathtest"),Integer.MAX_VALUE).filter(p -> p.endsWith("test.txt"));

findMyFile.forEach(System.out::println);
findMyFileSecond.forEach(System.out::println);

两个结果都包含相同的文件,并且两个方法几乎同时完成。 JavaDoc 说明如下:

此方法完全按照指定的方式遍历文件树 * #walk walk 方法与调用比较 * java.util.stream.Stream#filter 对 Stream 进行过滤 * 由 @code walk 方法返回,此方法可能更高效 * 避免对 BasicFileAttributes 的冗余检索

什么时候应该将walkfilter 结合使用,什么时候应该使用find?什么是最佳做法?

【问题讨论】:

The documentation 很清楚。 findwalk 更好,如果您只打算对 walk 返回的 Stream 应用过滤器。 【参考方案1】:

TL;DR:如果您需要按属性过滤文件/目录 - 使用 Files.find(),如果您不需要按文件属性过滤 - 使用 Files.walk()

详情

有一个轻微的差异,实际上在文档中进行了解释,但在某种程度上感觉完全错误。看源码就明白了:

Files.find:

return StreamSupport.stream(...)
                        .onClose(iterator::close)
                        .filter(entry -> matcher.test(entry.file(), entry.attributes()))
                        .map(entry -> entry.file());

Files.walk:

return StreamSupport.stream(...)
                        .onClose(iterator::close)
                        .map(entry -> entry.file());

这意味着,如果在您的最终过滤器中,您需要获取和验证文件属性 - File.find 可能会更快。那是因为使用File.walk,您的过滤器回调将需要额外调用例如Files.readAttributes(file, BasicFileAttributes.class),而使用 File.find - 属性已被检索并在过滤器回调中提供给您。

我刚刚在 Windows 上使用我的示例 10K-files-in-many-folders 结构通过搜索仅文件(即排除文件夹)对其进行了测试:

// pre-Java7/8 way via recursive listFiles (8037 files returned): 1521.657 msec.
for (File f : new File(dir).listFiles()) 
    if (f.isDirectory()) 
        _getFiles(files, path, pattern);
     else 
        ...
    


// Files.walk(8037 files returned): 1575.766823 msec.
try (Stream<Path> stream = Files.walk(path, Integer.MAX_VALUE) 
    files = stream.filter(p -> 
        if (Files.isDirectory(p))  return false;  // this extra check makes it much slower than Files.find
        ... 
    ).map(p -> p.toString()).collect(Collectors.toList());


// Files.find(8037 files returned): 27.606675 msec.
try (Stream<Path> stream = Files.find(path, Integer.MAX_VALUE, (p, a) -> !a.isDirectory())) 
    files = stream.filter(p ->  ... ).map(p -> p.toString()).collect(Collectors.toList());


// Files.walkFileTree(8037 returned): 27.443974 msec.
Files.walkFileTree(new File(path).toPath(), new SimpleFileVisitor<Path>()  
    @Override
    public FileVisitResult visitFile(Path p, BasicFileAttributes attrs) throws IOException 
        ...
        return FileVisitResult.CONTINUE;
    
);

【讨论】:

优秀,BasicFileAttributes 有他们的用例,isRegularFilelastModifiedTime 等等。【参考方案2】:

如果您需要在应用过滤器或并行化流之前对目录列表应用一些中间操作,我相信walk() 将是有利的。

【讨论】:

以上是关于Files.walk.filter 和 Files.find 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

Files.delete 和 Files.deleteIfExists 有点奇怪的行为

针对FILES和PATH的操作

如何将服务器端文件插入图像、fs.files 和 fs.chunks -GridFS (Ostrio/files)

C盘中Program Files 和Program Files (x86)区别

C盘中Program Files 和Program Files (x86)区别

php 单引号和双引号 $_FILES