Listen Sie Unterverzeichnisse mit flatMap auf
Wir haben bereits gesehen, wie man Dateien in einem bestimmten Verzeichnis auflistet. Schauen wir uns an, wie man die direkten Unterverzeichnisse des angegebenen Verzeichnisses (Tiefe ist 1) durchläuft, zuerst eine einfache Version implementiert und dann die bequemere Methode flatMap () zum Implementieren verwendet.
Wir verwenden zunächst eine herkömmliche for-Schleife, um ein bestimmtes Verzeichnis zu durchlaufen. Wenn das Unterverzeichnis Dateien enthält, fügen Sie diese zur Liste hinzu. Andernfalls fügen Sie das Unterverzeichnis zur Liste hinzu. Drucken Sie abschließend die Gesamtzahl aller Dateien aus. Der Code ist unten – dieser ist für den Hard-Modus.
Kopieren Sie den Codecode wie folgt:
öffentliche statische Void-ListeTheHardWay() {
List<File> files = new ArrayList<>();
File[] filesInCurrentDir = new File(".").listFiles();
for(File file : filesInCurrentDir) {
File[] filesInSubDir = file.listFiles();
if(filesInSubDir != null) {
files.addAll(Arrays.asList(filesInSubDir));
} anders {
files.add(file);
}
}
System.out.println("Count: " + files.size())
}
Wir rufen zuerst die Dateiliste im aktuellen Verzeichnis ab und durchlaufen sie dann. Wenn für jede Datei Unterdateien vorhanden sind, fügen Sie diese der Liste hinzu. Das ist kein Problem, aber es gibt einige häufige Probleme: Veränderlichkeit, Paranoia des Grundtyps, Imperativität, Ausführlichkeit des Codes usw. Eine kleine Methode namens flatMap() kann diese Probleme lösen.
Wie der Name schon sagt, wird diese Methode nach dem Mapping abgeflacht. Es ordnet Elemente in einer Sammlung genau wie map() zu. Aber im Gegensatz zur Map()-Methode gibt der Lambda-Ausdruck in der Map()-Methode nur ein Element zurück, und was hier zurückgegeben wird, ist ein Stream-Objekt. Diese Methode reduziert also mehrere Streams und ordnet jedes darin enthaltene Element einem reduzierten Stream zu.
Wir können flatMap() verwenden, um verschiedene Operationen auszuführen, aber das vorliegende Problem verdeutlicht seinen Wert. Jedes Unterverzeichnis verfügt über eine Liste oder einen Stream von Dateien, und wir möchten die Liste der Dateien in allen Unterverzeichnissen unter dem aktuellen Verzeichnis abrufen.
Einige Verzeichnisse sind möglicherweise leer oder haben keine untergeordneten Elemente. In diesem Fall verpacken wir das leere Verzeichnis oder die leere Datei in ein Stream-Objekt. Wenn wir eine Datei ignorieren möchten, kann die flatMap()-Methode im JDK auch leere Dateien sehr gut verarbeiten; sie fügt eine Nullreferenz als leere Sammlung in den Stream ein. Schauen wir uns die Verwendung der flatMap()-Methode an.
Kopieren Sie den Codecode wie folgt:
public static void betterWay() {
List<File> files =
Stream.of(new File(".").listFiles())
.flatMap(file -> file.listFiles() == null ?
Stream.of(file) : Stream.of(file.listFiles()))
.collect(toList());
System.out.println("Count: " + files.size());
}
Wir rufen zunächst den Unterdateistream des aktuellen Verzeichnisses ab und rufen dann dessen flatMap()-Methode auf. Übergeben Sie dann einen Lambda-Ausdruck an diese Methode, der einen Stream von Unterdateien der angegebenen Datei zurückgibt. Die Methode flatMap() gibt eine Sammlung von Dateien in allen Unterverzeichnissen des aktuellen Verzeichnisses zurück. Wir verwenden die Methoden „collect()“ und „toList()(“ in Collectors, um sie in einer Liste zu sammeln.
Der Lambda-Ausdruck, den wir an flatMap() übergeben, gibt eine Unterdatei einer Datei zurück. Wenn nicht, wird der Stream der Datei zurückgegeben. Die flatMap()-Methode ordnet diesen Stream elegant einer Sammlung von Streams zu, flacht die Sammlung dann ab und führt sie schließlich zu einem einzigen Stream zusammen.
Die flatMap()-Methode reduziert einen Großteil der Entwicklungsarbeit – sie kombiniert zwei aufeinanderfolgende Operationen, oft Tupel genannt, in einer eleganten Operation.
Wir wissen bereits, wie man mit der flatMap()-Methode alle Dateien in einem unmittelbaren Unterverzeichnis auflistet. Lassen Sie uns die Dateiänderungsvorgänge überwachen.
Überwachen Sie Dateiänderungen
Wir wissen bereits, wie man Dateien und Verzeichnisse findet, aber wenn wir umgehend Nachrichten erhalten möchten, wenn Dateien erstellt, geändert oder gelöscht werden, ist dies auch sehr einfach. Ein solcher Mechanismus ist sehr nützlich, um Änderungen in speziellen Dateien wie Konfigurationsdateien und Systemressourcen zu überwachen. Lassen Sie uns dieses in Java 7 eingeführte Tool WatchService erkunden, mit dem Dateiänderungen überwacht werden können. Viele der Funktionen, die wir unten sehen, stammen von JDK 7, aber die größte Verbesserung ist hier die Bequemlichkeit, die interne Iteratoren bieten.
Schreiben wir zunächst ein Beispiel für die Überwachung von Dateiänderungen im aktuellen Verzeichnis. Die Path-Klasse im JDK entspricht einer Instanz im Dateisystem, das eine Fabrik für Beobachterdienste ist. Wir können Benachrichtigungsereignisse für diesen Dienst registrieren, etwa so:
Kopieren Sie den Codecode wie folgt:
final Path path = Paths.get(".");
final WatchService watchService =
path.getFileSystem()
.newWatchService();
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
System.out.println("Melden Sie jede Datei, die innerhalb der nächsten 1 Minute geändert wurde...");
Wir haben einen WatchService registriert, um Änderungen am aktuellen Verzeichnis zu beobachten. Sie können diesen WatchService abfragen, um die Änderungsvorgänge der Dateien im Verzeichnis abzurufen, und er gibt diese Änderungen über einen WatchKey an uns zurück. Sobald wir den Schlüssel haben, können wir alle seine Ereignisse durchlaufen, um die Details der Dateiaktualisierung zu erhalten. Da möglicherweise mehrere Dateien gleichzeitig geändert werden, gibt der Abfragevorgang möglicherweise mehrere Ereignisse zurück. Werfen wir einen Blick auf den Polling- und Traversal-Code.
Kopieren Sie den Codecode wie folgt:
final WatchKey watchKey = watchService.poll(1, TimeUnit.MINUTES);
if(watchKey != null) {
watchKey.pollEvents()
.Strom()
.forEach(event ->
System.out.println(event.context()));
}
Wie Sie hier sehen können, erscheinen die Funktionen von Java 7 und Java 8 gleichzeitig. Wir konvertieren die von pollEvents() zurückgegebene Sammlung in einen Java 8 Stream und verwenden dann seinen internen Iterator, um detaillierte Aktualisierungsinformationen für jede Datei auszudrucken.
Lassen Sie uns diesen Code ausführen und dann die Datei „sample.txt“ im aktuellen Verzeichnis ändern, um zu sehen, ob das Programm dieses Update erkennen kann.
Kopieren Sie den Codecode wie folgt:
Melden Sie jede Datei, die innerhalb der nächsten 1 Minute geändert wurde ...
Beispiel.txt
Wenn wir diese Datei ändern, meldet das Programm, dass die Datei geändert wurde. Mit dieser Funktion können wir Aktualisierungen verschiedener Dateien überwachen und dann entsprechende Aufgaben ausführen. Natürlich können wir auch nur Dateierstellungs- oder -löschvorgänge registrieren.
Zusammenfassen
Mit Lambda-Ausdrücken und Methodenreferenzen werden häufige Aufgaben wie die Bearbeitung von Zeichenfolgen und Dateien sowie das Erstellen benutzerdefinierter Komparatoren einfacher und präziser. Anonyme innere Klassen werden elegant und die Variabilität verschwindet wie der Morgennebel nach Sonnenaufgang. Ein weiterer Vorteil der Codierung in diesem neuen Stil besteht darin, dass Sie die neuen Funktionen des JDK nutzen können, um große Verzeichnisse effizient zu durchsuchen.
Jetzt wissen Sie, wie Sie einen Lambda-Ausdruck erstellen und an eine Methode übergeben. Im nächsten Kapitel stellen wir vor, wie man funktionale Schnittstellen und Lambda-Ausdrücke zum Entwerfen von Software verwendet.