递归流

2022-09-02 10:09:47

我想使用Java 8以递归方式列出计算机上的所有文件。

Java 8 提供了一个返回所有文件和目录但不返回递归的方法。如何使用它来获取完整的递归文件列表(不使用变异集合)?listFiles

我已经尝试了下面的代码,但它只有一个层次的深度:

static Function<Path, Stream<Path>> listFiles = p -> {
    if (p.toFile().isDirectory()) {
        try { return Files.list(p); }
        catch (Exception e) { return Stream.empty(); }
    } else {
        return Stream.of(p);
    }
};

public static void main(String[] args) throws IOException {
    Path root = Paths.get("C:/temp/");
    Files.list(root).flatMap(listFiles).forEach(System.out::println);
}

而且使用不编译(不知道为什么)...return Files.list(p).flatMap(listFiles);

注意:我对涉及文件访问器或外部库的解决方案不感兴趣。


答案 1

通过递归遍历文件系统来生成路径流的新 API 是 。Files.walk

如果你真的想以递归方式生成一个流(不一定是遍历文件树,但我会继续使用它作为例子),那么使用方法引用完成递归可能会更简单一些:

class RecursiveStream {
    static Stream<Path> listFiles(Path path) {
        if (Files.isDirectory(path)) {
            try { return Files.list(path).flatMap(RecursiveStream::listFiles); }
            catch (Exception e) { return Stream.empty(); }
        } else {
            return Stream.of(path);
        }
    }

    public static void main(String[] args) {
        listFiles(Paths.get(".")).forEach(System.out::println);
    }
}

方法引用对于调整具有与该功能接口的功能接口相同的“形状”(参数和返回类型)的命名方法非常有用。这也避免了在实例或静态变量中存储 lambda 并以递归方式调用自身时潜在的初始化循环。


答案 2

显然不可能通过方法引用该函数定义中的函数,但它适用于 lambda。

所以在函数中,不编译,但编译。return Files.list(p).flatMap(listFiles);return Files.list(p).flatMap(q -> listFiles.apply(q));

这将以递归方式打印给定文件夹中的所有文件:

static final Function<Path, Stream<Path>> listFiles = p -> {
    if (p.toFile().isDirectory()) {
        try { return Files.list(p).flatMap(q -> listFiles.apply(q)); }
        catch (Exception e) { return Stream.empty(); }
    } else {
        return Stream.of(p);
    }
};

public static void main(String[] args) throws IOException {
    Path root = Paths.get("C:/temp/");
    Files.list(root).flatMap(listFiles).forEach(System.out::println);
}

但正如所指出的,这是不必要的:

Files.walk(root).forEach(System.out::println);

做同样的事情...