2014-02-08 22 views
17

Ich möchte alle Dateien auf meinem Computer Java rekursiv 8.rekursive Strom

Java 8 stellt eine listFiles Methode aufzulisten, die die Dateien und Verzeichnisse alle, aber ohne Rekursion zurückgibt. Wie kann ich es verwenden, um eine vollständige rekursive Liste von Dateien zu erhalten (ohne eine mutierende Sammlung zu verwenden)?

Ich habe den Code unten versucht, aber es geht nur eine Ebene tiefer:

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); 
} 

Und return Files.list(p).flatMap(listFiles); mit nicht kompiliert (nicht sicher, warum) ...

Hinweis: Ich bin nicht interessiert in Lösungen mit FileVisitors oder externen Bibliotheken.

+1

'Files.walkFileTree'? Oder möchten Sie wirklich nur einen rekursiven Stream verwenden? :-) –

+1

@StuartMarks Ja, ich möchte nur einen rekursiven Stream verwenden! Und WalkFileTree ist ziemlich ausführlich, also habe ich versucht, einen "Einzeiler" zu finden. – assylias

+1

Whoops, ich meinte 'Files.walk'. Es nimmt einen Pfad und gibt einen Stream zurück. –

Antwort

17

Eine neue API zum Generieren eines Pfadstroms, indem das Dateisystem rekursiv durchlaufen wird, lautet Files.walk.

: die Rekursion mit der Methode Referenzen, könnte es ein bisschen einfacher sein

Wenn Sie wirklich rekursiv einen Strom erzeugen wollen (mit, dass als Beispiel nicht unbedingt den Dateibaum gehen, aber ich werde auch weiterhin) zu erreichen

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); 
    } 
} 

Methodenreferenzen erweisen sich als sehr nützlich, um eine benannte Methode mit derselben "Form" (Argumente und Rückgabetyp) als funktionale Schnittstelle zu dieser funktionalen Schnittstelle anzupassen. Dies vermeidet auch die potentielle Initialisierungszirkularität, indem ein Lambda in einer Instanz oder statischen Variable gespeichert wird und sich selbst rekursiv aufruft.

3

Es ist offensichtlich nicht möglich, über eine Methodenreferenz auf eine Funktion innerhalb dieser Funktionsdefinition zu verweisen, aber sie funktioniert mit einem Lambda.

Also in der Funktion, return Files.list(p).flatMap(listFiles); kompiliert nicht, aber return Files.list(p).flatMap(q -> listFiles.apply(q)); tut.

Dies druckt alle Dateien in den angegebenen Ordner rekursiv:

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); 
} 

aber wie erwähnt, ist dies nicht notwendig:

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

tut das gleiche ...

+0

Es kompiliert nicht auf JDK 8 b127 entweder: 'Kann ein Feld nicht referenzieren, bevor es definiert wurde. – nobeh

+0

Es kompiliert auf b128 – assylias

+0

Nur um zu bemerken: Dies beruht auf' listFiles 'ist ein statisches Feld, wie im Beispiel gezeigt, und würde in anderen Fällen nicht funktionieren. Trotzdem, mit der neuesten JDK, konnte ich es nicht kompilieren, wenn ich zuerst das Feld 'null' initialisiert und dann drehte sich um und neu initialisiert es mit dem Lambda. Natürlich scheint das alles sehr stinkend zu sein. – jkschneider