Compter le nombre de fichiers dans un répertoire en utilisant Java

Comment est-ce que je compte le nombre de fichiers dans un répertoire utilisant Java? Pour simplifier, supposons que le répertoire ne comporte aucun sous-répertoire.

Je connais la méthode standard de:

new File().listFiles().length 

Mais cela permettra de parcourir tous les fichiers du répertoire, ce qui pourrait prendre du temps si le nombre de fichiers est important. De plus, je ne me soucie pas des fichiers réels dans le répertoire, à moins que leur nombre ne soit supérieur à un grand nombre fixe (disons 5000).

Je suppose, mais le répertoire (ou son i-node dans le cas d’Unix) ne stocke-t-il pas le nombre de fichiers qu’il contient? Si je pouvais obtenir ce nombre directement du système de fichiers, ce serait beaucoup plus rapide. Je dois faire cette vérification pour chaque requête HTTP sur un serveur Tomcat avant que le back-end ne commence le traitement réel. Par conséquent, la vitesse est d’une importance primordiale.

Je pourrais exécuter un démon de temps en temps pour effacer le répertoire. Je le sais, alors s’il te plaît, ne me donne pas cette solution.

Cela peut ne pas convenir à votre application, mais vous pouvez toujours essayer un appel natif (en utilisant jni ou jna ), ou bien exécuter une commande spécifique à la plateforme et lire la sortie avant de revenir à list (). Length. Sur * nix, vous pouvez ls -1a | wc -l ls -1a | wc -l (note – c’est un tiret-un-un pour la première commande, et un tiret-minuscule-L pour le second). Pas sûr de ce qui serait juste sur Windows – peut-être juste un dir et chercher le résumé.

Avant de vous préoccuper de quelque chose comme ceci, je vous recommande fortement de créer un répertoire avec un très grand nombre de fichiers et de voir si la longueur de list () est trop longue. Comme le suggère ce blogueur , vous ne voudrez peut-être pas transpirer.

J’irais probablement avec la réponse de Varkhan moi-même.

Ah … la raison pour ne pas avoir une méthode simple en Java est l’abstraction du stockage de fichiers: certains systèmes de fichiers peuvent ne pas avoir facilement access au nombre de fichiers dans un répertoire … voir par exemple les systèmes de fichiers P2P dissortingbués, fs qui stockent les listes de fichiers sous forme de liste chaînée ou les systèmes de fichiers sauvegardés dans une firebase database …). Donc oui,

 new File().list().length 

est probablement votre meilleur pari.

Depuis Java 8, vous pouvez le faire en trois lignes:

 try (Stream files = Files.list(Paths.get("your/path/here"))) { long count = files.count(); } 

Concernant les 5000 nœuds enfants et les aspects inodes:

Cette méthode va parcourir les entrées, mais comme Varkhan l’a suggéré, vous ne pourrez probablement pas faire mieux que jouer avec JNI ou les commandes directes du système, mais même alors, vous ne pouvez jamais être sûr que ces méthodes ne font pas la même chose!

Cependant, creusons un peu:

En examinant le source JDK8, Files.list expose un stream utilisant une version de Files.newDirectoryStream qui délègue à FileSystemProvider.newDirectoryStream .

Sur les systèmes UNIX ( sun.nio.fs.UnixFileSystemProvider.class décompilés), il charge un iterator: Un sun.nio.fs.UnixSecureDirectoryStream est utilisé (avec des verrous de fichiers lors de la itération dans le répertoire).

Donc, il y a un iterator qui va parcourir les entrées ici.

Maintenant, regardons le mécanisme de comptage.

Le nombre réel est effectué par l’API de réduction de nombre / sum exposée par les stream Java 8 . En théorie, cette API peut effectuer des opérations parallèles sans trop d’effort (avec multi-lecture). Cependant, le stream est créé avec le parallélisme désactivé, donc c’est un non-droit …

Le bon côté de cette approche est qu’elle ne charge pas le tableau en mémoire car les entrées seront comptabilisées par un iterator lorsqu’elles sont lues par l’API sous-jacente (Filesystem).

Enfin, pour l’information, conceptuellement dans un système de fichiers, un nœud de répertoire n’est pas obligé de contenir le nombre de fichiers qu’il contient, il peut juste contenir la liste de ses nœuds enfants (liste des inodes). Je ne suis pas un expert des systèmes de fichiers, mais je pense que les systèmes de fichiers UNIX fonctionnent comme ça. Vous ne pouvez donc pas supposer qu’il existe un moyen d’avoir directement ces informations (par exemple: il peut toujours y avoir une liste de nœuds enfants quelque part).

Malheureusement, je pense que c’est déjà le meilleur moyen (bien que list() soit légèrement meilleur que listFiles() , car il ne construit pas d’objects de File ).

Comme vous n’avez pas vraiment besoin du nombre total et que vous voulez en fait effectuer une action après un certain nombre (dans votre cas, 5000), vous pouvez utiliser java.nio.file.Files.newDirectoryStream . L’avantage est que vous pouvez quitter tôt plutôt que de devoir parcourir l’ensemble du répertoire pour obtenir un compte.

 public boolean isOverMax(){ Path dir = Paths.get("C:/foo/bar"); int i = 1; try (DirectoryStream stream = Files.newDirectoryStream(dir)) { for (Path p : stream) { //larger than max files, exit if (++i > MAX_FILES) { return true; } } } catch (IOException ex) { ex.printStackTrace(); } return false; } 

L’ interface doc pour DirectoryStream également quelques bons exemples.

Si vous avez des répertoires contenant vraiment (> 100’000) de nombreux fichiers, voici un moyen (non portable) de procéder:

 Ssortingng directoryPath = "a path"; // -f flag is important, because this way ls does not sort it output, // which is way faster Ssortingng[] params = { "/bin/sh", "-c", "ls -f " + directoryPath + " | wc -l" }; Process process = Runtime.getRuntime().exec(params); BufferedReader reader = new BufferedReader(new InputStreamReader( process.getInputStream())); Ssortingng fileCount = reader.readLine().sortingm() - 2; // accounting for .. and . reader.close(); System.out.println(fileCount); 

Utiliser sigar devrait aider. Sigar a des crochets natifs pour obtenir les statistiques

 new Sigar().getDirStat(dir).getTotal() 

Malheureusement, comme l’a dit mmyers, File.list () est à peu près aussi rapide que Java. Si la vitesse est aussi importante que vous le dites, vous pouvez envisager de réaliser cette opération particulière en utilisant JNI . Vous pouvez ensuite adapter votre code à votre situation et à votre système de fichiers particuliers.

 public void shouldGetTotalFilesCount() { Integer reduce = of(listRoots()).parallel().map(this::getFilesCount).reduce(0, ((a, b) -> a + b)); } private int getFilesCount(File directory) { File[] files = directory.listFiles(); return Objects.isNull(files) ? 1 : Stream.of(files) .parallel() .reduce(0, (Integer acc, File p) -> acc + getFilesCount(p), (a, b) -> a + b); }