Рекурсивный поток
Я хочу перечислить все файлы на моем компьютере рекурсивно, используя 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);
не компилируется (не уверен, почему)...
Примечание. Меня не интересуют решения, связанные с FileVisitors или внешними библиотеками.
Ответы
Ответ 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);
}
}
Ссылки на методы оказываются весьма полезными для адаптации именованного метода, который имеет одинаковую "форму" (аргументы и тип возврата) в качестве функционального интерфейса к этому функциональному интерфейсу. Это также позволяет избежать потенциальной цикличности инициализации, сохраняя лямбда в экземпляре или статической переменной и вызывая себя рекурсивно.
Ответ 2
По-видимому, невозможно ссылаться на функцию в определении этой функции с помощью ссылки на метод, но она работает с лямбдой.
Итак, в функции 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);
делает то же самое...