7.3 文件系统的遍历

问题

用户希望对文件系统进行深度优先遍历(depth-first traversal)。

方案

使用 java.nio.file.Files 类定义的静态方法 walk

讨论

walk 方法的签名如下:

  1. public static Stream<Path> walk(Path start,
  2. FileVisitOption... options)
  3. throws IOException

walk 方法的参数为起始 Path 以及 FileVisitOption 值的可变参数列表。从起始路径开始对文件系统执行深度优先遍历,返回一个由 Path 实例惰性填充的 Stream

由于返回的 Stream 封装了 DirectoryStream,仍然建议在 try-with-resources 代码块中使用 walk 方法,如例 7-7 所示。

例 7-7 文件树的遍历

  1. try (Stream<Path> paths = Files.walk(Paths.get("src/main/java"))) {
  2. paths.forEach(System.out::println);
  3. } catch (IOException e) {
  4. e.printStackTrace();
  5. }

walk 方法传入零个或多个 FileVisitOption 值作为第二个参数以及后续参数,不过本例并未使用任何 FileVisitOption 值。FileVisitOption 是 Java 1.7 引入的一种枚举类型,所包含的唯一枚举常量为 FOLLOW_LINKS。至少从理论上说,FOLLOW_LINKS 意味着文件树中可能存在循环,因此流将跟踪所访问的文件。一旦检测到循环,程序将抛出 FileSystemLoopException

执行本书配套的源代码(例 7-7),输出类似于:

  1. src/main/java
  2. src/main/java/collectors
  3. src/main/java/collectors/Actor.java
  4. src/main/java/collectors/AddCollectionToMap.java
  5. src/main/java/collectors/Book.java
  6. src/main/java/collectors/CollectorsDemo.java
  7. src/main/java/collectors/ImmutableCollections.java
  8. src/main/java/collectors/Movie.java
  9. src/main/java/collectors/MysteryMen.java
  10. src/main/java/concurrency
  11. src/main/java/concurrency/CommonPoolSize.java
  12. src/main/java/concurrency/CompletableFutureDemos.java
  13. src/main/java/concurrency/FutureDemo.java
  14. src/main/java/concurrency/ParallelDemo.java
  15. src/main/java/concurrency/SequentialToParallel.java
  16. src/main/java/concurrency/Timer.java
  17. src/main/java/datetime
  18. ...

程序采用惰性方式遍历路径,所生成的流必然包含至少一个元素(起始参数)。对于遇到的每条路径,程序将判断它是否为目录,是则遍历其中的所有条目,然后移动到下一个同级元素(sibling)。其结果是一种深度优先遍历。程序访问每个目录包含的所有条目之后,将关闭该目录。

walk 方法还包括以下重载形式:

  1. public static Stream<Path> walk(Path start,
  2. int maxDepth,
  3. FileVisitOption... options)
  4. throws IOException

其中,参数 maxDepth 是要访问的目录级别的最大值,0 表示只访问起始文件。如果不使用 maxDepth,可以通过 Integer.MAX_VALUE 值来指定应访问所有级别。

另见

有关如何列出一个目录中的所有文件请参见范例 7.2,有关文件搜索的讨论请参见范例 7.4。