Répertorier les sous-répertoires à l'aide de flatMap
Nous avons déjà vu comment lister les fichiers dans un répertoire spécifié. Voyons comment parcourir les sous-répertoires directs du répertoire spécifié (la profondeur est de 1), implémentons d'abord une version simple, puis utilisons la méthode flatMap() plus pratique pour l'implémenter.
Nous utilisons d’abord une boucle for traditionnelle pour parcourir un répertoire spécifié. S'il y a des fichiers dans le sous-répertoire, ajoutez-les à la liste ; sinon, ajoutez le sous-répertoire à la liste. Enfin, imprimez le nombre total de tous les fichiers. Le code est ci-dessous – celui-ci est pour le mode difficile.
Copiez le code comme suit :
public static void listTheHardWay() {
List<File> files = new ArrayList<>();
File[] filesInCurrentDir = new File(".").listFiles();
pour (Fichier fichier : filesInCurrentDir) {
File[] filesInSubDir = file.listFiles();
if(filesInSubDir != null) {
files.addAll(Arrays.asList(filesInSubDir));
} autre {
fichiers.ajouter(fichier);
}
}
System.out.println("Nombre : " + files.size())
}
Nous obtenons d’abord la liste des fichiers dans le répertoire courant, puis nous la parcourons. Pour chaque fichier, s'il comporte des sous-fichiers, ajoutez-les à la liste. Cela ne pose aucun problème, mais cela présente quelques problèmes communs : mutabilité, paranoïa des types de base, impératif, verbosité du code, etc. Une petite méthode appelée flatMap() peut résoudre ces problèmes.
Comme son nom l'indique, cette méthode s'aplatit après le mappage. Il mappe les éléments d'une collection tout comme map(). Mais contrairement à la méthode map(), l'expression lambda dans la méthode map() ne renvoie qu'un élément, et ce qui est renvoyé ici est un objet Stream. Ainsi, cette méthode aplatit plusieurs flux et mappe chaque élément à l'intérieur sur un flux aplati.
Nous pouvons utiliser flatMap() pour effectuer diverses opérations, mais le problème en question illustre sa valeur. Chaque sous-répertoire a une liste ou un flux de fichiers, et nous souhaitons obtenir la liste des fichiers dans tous les sous-répertoires du répertoire actuel.
Certains répertoires peuvent être vides ou n'avoir aucun élément enfant. Dans ce cas, nous encapsulons le répertoire ou le fichier vide dans un objet flux. Si nous voulons ignorer un fichier, la méthode flatMap() du JDK peut également très bien gérer les fichiers vides ; elle fusionnera une référence nulle dans le flux en tant que collection vide ; Regardons l'utilisation de la méthode flatMap().
Copiez le code comme suit :
public static void betterWay() {
Liste<Fichier> fichiers =
Stream.of(new File(".").listFiles())
.flatMap(file -> file.listFiles() == null ?
Stream.of(fichier) : Stream.of(file.listFiles()))
.collect(toList());
System.out.println("Count: " + files.size());
}
Nous obtenons d’abord le flux de sous-fichier du répertoire courant, puis appelons sa méthode flatMap(). Transmettez ensuite une expression lambda à cette méthode, qui renverra un flux de sous-fichiers du fichier spécifié. La méthode flatMap() renvoie une collection de fichiers dans tous les sous-répertoires du répertoire courant. Nous utilisons la méthode collect() et la méthode toList()( dans Collectors pour les collecter dans une liste.
L'expression lambda que nous transmettons à flatMap() renvoie un sous-fichier d'un fichier. Sinon, le flux du fichier est renvoyé. La méthode flatMap() mappe élégamment ce flux dans une collection de flux, puis aplatit la collection et enfin la fusionne en un seul flux.
La méthode flatMap() réduit beaucoup de travail de développement - elle combine deux opérations consécutives, souvent appelées tuples - en une seule opération élégante.
Nous savons déjà comment utiliser la méthode flatMap() pour lister tous les fichiers d'un sous-répertoire immédiat. Surveillons les opérations de modification des fichiers.
Surveiller les modifications de fichiers
Nous savons déjà comment trouver des fichiers et des répertoires, mais si nous voulons recevoir des messages d'invite lorsque des fichiers sont créés, modifiés ou supprimés, c'est également très simple. Un tel mécanisme est très utile pour surveiller les modifications apportées à des fichiers spéciaux tels que les fichiers de configuration et les ressources système. Explorons cet outil introduit dans Java 7, WatchService, qui peut être utilisé pour surveiller les modifications de fichiers. La plupart des fonctionnalités que nous voyons ci-dessous proviennent du JDK 7, mais la plus grande amélioration ici est la commodité apportée par les itérateurs internes.
Écrivons d'abord un exemple de surveillance des modifications de fichiers dans le répertoire courant. La classe Path du JDK correspond à une instance du système de fichiers, qui est une usine pour les services d'observateurs. Nous pouvons enregistrer des événements de notification pour ce service, comme ceci :
Copiez le code comme suit :
Chemin initial path = Paths.get(".");
WatchService final watchService =
chemin.getFileSystem()
.newWatchService();
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
System.out.println("Signaler tout fichier modifié dans la minute suivante...");
Nous avons enregistré un WatchService pour observer les modifications apportées au répertoire actuel. Vous pouvez interroger ce WatchService pour obtenir les opérations de modification des fichiers du répertoire, et il nous renverra ces modifications via une WatchKey. Une fois que nous avons la clé, nous pouvons parcourir tous ses événements pour obtenir les détails de la mise à jour du fichier. Étant donné que plusieurs fichiers peuvent être modifiés en même temps, l'opération d'interrogation peut renvoyer plusieurs événements. Jetons un coup d'œil au code d'interrogation et de traversée.
Copiez le code comme suit :
WatchKey final watchKey = watchService.poll(1, TimeUnit.MINUTES);
si(watchKey != null) {
watchKey.pollEvents()
.flux()
.forEach(événement ->
System.out.println(event.context()));
}
Comme vous pouvez le voir ici, les fonctionnalités de Java 7 et Java 8 apparaissent en même temps. Nous convertissons la collection renvoyée par pollEvents() en un flux Java 8, puis utilisons son itérateur interne pour imprimer des informations de mise à jour détaillées pour chaque fichier.
Exécutons ce code, puis modifions le fichier sample.txt dans le répertoire actuel pour voir si le programme peut détecter cette mise à jour.
Copiez le code comme suit :
Signalez tout fichier modifié dans la minute suivante...
exemple.txt
Lorsque nous modifions ce fichier, le programme nous demandera que le fichier a été modifié. Nous pouvons utiliser cette fonctionnalité pour surveiller les mises à jour de différents fichiers, puis effectuer les tâches correspondantes. Bien entendu, nous pouvons également enregistrer uniquement les opérations de création ou de suppression de fichiers.
Résumer
Avec les expressions lambda et les références de méthodes, les tâches courantes telles que la manipulation de chaînes et de fichiers et la création de comparateurs personnalisés deviennent plus faciles et plus concises. Les classes internes anonymes deviennent élégantes et la variabilité disparaît comme la brume matinale après le lever du soleil. Un autre avantage du codage dans ce nouveau style est que vous pouvez utiliser les nouvelles fonctionnalités du JDK pour parcourir efficacement de grands répertoires.
Vous savez maintenant comment créer une expression lambda et la transmettre à une méthode. Dans le chapitre suivant, nous présenterons comment utiliser les interfaces fonctionnelles et les expressions lambda pour concevoir des logiciels.