[疯狂Java]NIO.2:walkFileTree、FileVisitor(遍历文件/目录)

介绍NIO.2中Files工具类提供的walkFileTree方法,用于高效遍历文件系统。通过FileVisitor接口定义访问行为,包括访问目录前后及访问文件的操作。

1. 遍历文件和目录——FileVisitor:

    1) 在旧版本中遍历文件系统只能通过递归的方法来实现,但是这种方法不仅消耗资源大而且效率低;

    2) NIO.2的Files工具类提供了一个静态工具方法walkFileTree来高效并优雅地遍历文件系统;

    3) walkFileTree:

         i. 原型:static Path Files.walkFileTree(Path start, FileVisitor<? super Path> visitor);

         ii. 表示从start代表的节点开始遍历文件系统;

         iii. 其中visitor是遍历过程中的行为控制器;

    4) 遍历行为控制器——FileVisitor:

         i. 它是一个接口,里面定义了4个方法用来指定当你访问一个节点之前、之中、之后、失败时应该采取什么行动;

         ii. 这个设计非常优雅和科学,毕竟你在遍历文件系统时想要做的事情无外乎发生在这几个时间点上,Java全部为你考虑好了,并搭好了框架!多么的贴心!!

         iii. API:

public interface FileVisitor<T> {

    FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) // 访问一个目录前要干啥
        throws IOException;
    
    FileVisitResult postVisitDirectory(T dir, IOException exc) // 访问一个目录后要干啥
            throws IOException;

    FileVisitResult visitFile(T file, BasicFileAttributes attrs) // 正在访问一个文件时要干啥
        throws IOException;
    
    FileVisitResult visitFileFailed(T file, IOException exc) // 访问一个文件失败时要干啥
        throws IOException;
}
!!注意,里面的文件和目录要区别开;

!可以看到它是一个模板接口,但是在walkFileTree中它的类型已经确定了就是Path,因此里面的T类型就是Path类型了;

        iv. 接口方法的返回值FileVisitResult是一个枚举类型,walkFileTree就是会根据这个返回值决定是否要继续遍历下去,如果要继续遍历则应该怎样遍历,它总共有4个枚举值:都是FileVisitResult中定义的枚举值

CONTINUE:继续遍历

SKIP_SIBLINGS:继续遍历,但忽略当前节点的所有兄弟节点直接返回上一层继续遍历

SKIP_SUBTREE:继续遍历,但是忽略子目录,但是子文件还是会访问;

TERMINATE:终止遍历

    5) 实际使用中通常直接继承FileVisitor的适配器SimpleFileVisitor,因为只需要实现你感兴趣的方法即可,无需4个方法全部都实现;

    6) 示例:

public class Test {
	
	public static void main(String[] args) throws IOException {
		
		Files.walkFileTree(Paths.get(".."), new SimpleFileVisitor<Path>() {

			@Override
			public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
				// TODO Auto-generated method stub
				// return super.preVisitDirectory(dir, attrs);
				System.out.println("正在访问:" + dir + "目录");
				return FileVisitResult.CONTINUE;
			}

			@Override
			public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
				// TODO Auto-generated method stub
				// return super.visitFile(file, attrs);
				System.out.println("\t正在访问" + file + "文件");
				if (file.endsWith("Test.java")) {
					System.out.println("******找到目标文件Test.java******");
					return FileVisitResult.TERMINATE; // 找到了就终止
				}
				return FileVisitResult.CONTINUE; // 没找到继续找
			}
			
		});
	}
}
!!Path可以像String那样进行字符串校验,校验路径中的字符串:都是Path的对象方法

        a. boolean startsWith(String other); // 前缀检查

        b. boolean startsWith(Path other);

        c. boolean endsWith(String other); // 后缀检查

        d. boolean endsWith(Path other);

!!比较的都是其path成员字符串里的内容,并不会深入文件系统用完整的绝对路径来比较

<think>我们正在使用java.nio.file包中的类来操作文件目录。用户想要查找一个Path下的子文件文件夹。 在Java NIO中,我们可以使用Files类提供的方法来遍历目录。主要有两种方式: 1. 使用Files.list(Path dir)方法:返回一个Stream<Path>,包含目录中的条目(文件和子目录)。注意:它不会递归遍历目录2. 使用Files.walk(Path start, int maxDepth, FileVisitOption... options)方法:可以递归遍历目录树,通过maxDepth参数控制遍历深度。 3. 使用Files.newDirectoryStream(Path dir)方法:返回一个DirectoryStream<Path>,用于遍历目录中的文件(非递归)。 另外,还可以使用Files.walkFileTree(Path start, FileVisitor<? super Path> visitor)方法,通过实现FileVisitor接口来定制遍历行为(例如,在访问每个文件目录时执行操作)。 根据引用[4]中的示例,我们可以看到使用Files.walkFileTree并重写visitFile方法可以遍历文件。但是用户需要的是获取子文件文件夹,我们可以选择不同的方法。 下面我将分别介绍这些方法: 方法一:使用Files.list (非递归,只获取直接子项) 示例代码: Path dir = Paths.get("your_directory_path"); try (Stream<Path> stream = Files.list(dir)) { stream.forEach(path -> { if (Files.isDirectory(path)) { System.out.println("目录: " + path); } else { System.out.println("文件: " + path); } }); } catch (IOException e) { e.printStackTrace(); } 方法二:使用Files.walk进行递归遍历(可以设置深度,如果深度为1,则等同于非递归) Path dir = Paths.get("your_directory_path"); // 设置深度为1,表示只获取直接子项 try (Stream<Path> stream = Files.walk(dir, 1)) { // 跳过起始目录本身,因为walk方法会包含起始目录 stream.filter(p -> !p.equals(dir)) .forEach(path -> { if (Files.isDirectory(path)) { System.out.println("目录: " + path); } else { System.out.println("文件: " + path); } }); } catch (IOException e) { e.printStackTrace(); } 方法三:使用Files.newDirectoryStream (非递归) Path dir = Paths.get("your_directory_path"); try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { for (Path path : stream) { if (Files.isDirectory(path)) { System.out.println("目录: " + path); } else { System.out.println("文件: " + path); } } } catch (IOException e) { e.printStackTrace(); } 方法四:使用Files.walkFileTree (递归,可定制) Path dir = Paths.get("your_directory_path"); Files.walkFileTree(dir, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { System.out.println("文件: " + file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { // 这里排除起始目录本身,因为用户可能不需要 if (!dir.equals(dir)) { System.out.println("目录: " + dir); } return FileVisitResult.CONTINUE; } }); 注意:在walkFileTree中,起始目录也会被访问(preVisitDirectory),所以如果我们不想包括起始目录,可以跳过它。 根据用户需求,如果只需要直接子文件文件夹(非递归),那么方法一(Files.list)或方法三(DirectoryStream)比较合适。如果需要递归遍历,则方法二(Files.walk)或方法四(Files.walkFileTree)更合适。 另外,引用[3]提到了使用SimpleFileVisitor遍历文件树,并重写相应的方法(如visitFile和postVisitDirectory)来实现操作,如删除等。这里我们只是列出文件目录。 下面我将给出一个完整的示例,展示如何获取一个目录的直接子项(非递归): </think> ### 使用 `java.nio.file.Path` 查找子文件文件夹的方法 在 Java NIO 中,有几种高效的方式可以查找目录中的子文件和子文件夹: #### 1. **`Files.list()` 方法(非递归)** ```java Path dir = Paths.get("/your/directory"); try (Stream<Path> stream = Files.list(dir)) { stream.forEach(path -> { if (Files.isDirectory(path)) { System.out.println("子文件: " + path); } else { System.out.println("子文件: " + path); } }); } catch (IOException e) { e.printStackTrace(); } ``` **特点**: - 非递归遍历(仅当前目录) - 使用 `try-with-resources` 自动关闭流 - 结合 `Files.isDirectory()` 判断类型[^1][^4] #### 2. **`Files.walk()` 方法(递归遍历)** ```java int maxDepth = 3; // 遍历深度 try (Stream<Path> stream = Files.walk(dir, maxDepth)) { stream.filter(Files::isRegularFile) // 只获取文件 .forEach(System.out::println); stream.filter(Files::isDirectory) // 只获取文件.forEach(System.out::println); } catch (IOException e) { e.printStackTrace(); } ``` **参数说明**: - `maxDepth=1`:仅当前目录 - `maxDepth=Integer.MAX_VALUE`:完全递归 #### 3. **`Files.newDirectoryStream()`(低内存消耗)** ```java try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { for (Path path : stream) { if (Files.isDirectory(path)) { System.out.println("文件: " + path); } } } catch (IOException e) { e.printStackTrace(); } ``` **优势**: - 适合超大目录(非流式API) - 支持过滤器(如 `*.txt`): ```java Files.newDirectoryStream(dir, "*.{java,class}") ``` #### 4. **`Files.walkFileTree()`(完全控制遍历)** ```java Files.walkFileTree(dir, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { System.out.println("文件: " + file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { if (!dir.equals(startDir)) { // 跳过起始目录 System.out.println("进入文件: " + dir); } return FileVisitResult.CONTINUE; } }); ``` **适用场景**[^3][^4]: - 需要精细控制遍历过程(如跳过特定目录) - 需要在进入/退出目录时执行操作 - 处理符号链接等特殊情况 #### 性能对比表 | 方法 | 递归支持 | 内存占用 | 适用场景 | |-----------------------|----------|----------|------------------------------| | `Files.list()` | ❌ | 低 | 快速获取直接子项 | | `Files.walk()` | ✅ | 中 | 简单递归遍历 | | `newDirectoryStream()`| ❌ | 低 | 超大目录或需要过滤 | | `walkFileTree()` | ✅ | 高 | 需要完全控制遍历过程 | > **最佳实践**: > - 非递归遍历优先选 `Files.list()` > - 递归遍历优先选 `Files.walk()` > - 需要过滤或特殊处理时用 `walkFileTree()`[^4]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值