2014-02-08 5 views
17

Я хочу, чтобы перечислить все файлы на компьютере рекурсивно с использованием 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

'Files.walkFileTree'? Или вы действительно просто хотите использовать рекурсивный поток? :-) –

+1

@StuartMarks Да, я просто хочу использовать рекурсивный поток! И walkFileTree довольно многословный, поэтому я пытался найти «однострочный». – assylias

+1

Упс, я имел в виду 'Files.walk'. Он берет Путь и возвращает поток . –

ответ

17

Новый 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 в экземпляре или статической переменной и вызывая себя рекурсивно.

3

По-видимому, невозможно ссылаться на функцию в определении этой функции с помощью ссылки на метод, но она работает с лямбдой.

Так что в функции 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); 

делает то же самое ...

+0

Он также не компилируется на JDK 8 b127: 'Не может ссылаться на поле до его определения.' – nobeh

+0

Он компилируется на b128 – assylias

+0

Просто отметить: это зависит от 'listFiles 'как статическое поле, как показано в примере, и не будет работать в других случаях. Несмотря на это, с последним JDK я не смог его компилировать, если только я не инициализировал поле «null», а затем развернулся и повторно инициализировал его с помощью лямбда. Конечно, все это кажется очень вонючим. – jkschneider

Смежные вопросы