Contando la cantidad de archivos en un directorio usando Java

¿Cómo cuento la cantidad de archivos en un directorio usando Java? Para simplificar, supongamos que el directorio no tiene ningún subdirectorio.

Conozco el método estándar de:

new File().listFiles().length 

Pero esto pasará efectivamente por todos los archivos en el directorio, lo que puede llevar mucho tiempo si la cantidad de archivos es grande. Además, no me importan los archivos reales en el directorio a menos que su número sea mayor que un número grande fijo (digamos 5000).

Estoy adivinando, pero ¿el directorio (o su i-node en el caso de Unix) no almacena la cantidad de archivos que contiene? Si pudiera obtener ese número directamente del sistema de archivos, sería mucho más rápido. Necesito hacer esta comprobación para cada solicitud HTTP en un servidor Tomcat antes de que el back-end comience a hacer el procesamiento real. Por lo tanto, la velocidad es de sum importancia.

Podría ejecutar un daemon de vez en cuando para borrar el directorio. Lo sé, así que por favor no me des esa solución.

Esto puede no ser apropiado para su aplicación, pero siempre puede intentar una llamada nativa (usando jni o jna ), o ejecutar un comando específico de la plataforma y leer la salida antes de volver a la longitud de la lista (). En * nix, podría ejecutar ls -1a | wc -l ls -1a | wc -l (nota – eso es dash-one-a para el primer comando, y dash-minúscula-L para el segundo). No estoy seguro de lo que sería correcto en Windows, quizás solo un dir y busque el resumen.

Antes de molestarme con algo así, le recomiendo encarecidamente que cree un directorio con una gran cantidad de archivos y solo vea si list (). Length realmente lleva demasiado tiempo. Como sugiere este blogger , es posible que no desee sudar esto.

Probablemente también vaya con la respuesta de Varkhan.

Ah … la razón para no tener un método sencillo en Java para hacer eso es la abstracción de almacenamiento de archivos: algunos sistemas de archivos pueden no tener la cantidad de archivos en un directorio disponible … ese recuento puede no tener ningún significado ( ver, por ejemplo, sistemas de archivos P2P distribuidos, fs que almacenan listas de archivos como una lista vinculada, o sistemas de archivos respaldados por bases de datos …). Entonces sí,

 new File().list().length 

es probablemente tu mejor apuesta.

Desde Java 8, puedes hacer eso en tres líneas:

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

En cuanto a los 5000 nodos secundarios y aspectos de inode:

Este método iterará sobre las entradas, pero como sugirió Varkhan, probablemente no puedas hacer nada mejor además de jugar con JNI o ​​dirigir llamadas a comandos del sistema, ¡pero aun así nunca puedes estar seguro de que estos métodos no hagan lo mismo!

Sin embargo, profundicemos en esto un poco:

En cuanto a la fuente JDK8, Files.list expone una secuencia que utiliza un Iterable de Files.newDirectoryStream que delega a FileSystemProvider.newDirectoryStream .

En sistemas UNIX (descomstackdo sun.nio.fs.UnixFileSystemProvider.class ), carga un iterador: se usa sun.nio.fs.UnixSecureDirectoryStream (con lockings de archivos mientras se itera a través del directorio).

Entonces, hay un iterador que recorrerá las entradas aquí.

Ahora, veamos el mecanismo de conteo.

El recuento real se realiza mediante la API de reducción de recuento / sum expuesta por las secuencias de Java 8 . En teoría, esta API puede realizar operaciones paralelas sin mucho esfuerzo (con multihtreading). Sin embargo, la secuencia se crea con el paralelismo deshabilitado, por lo que es un no ir …

El lado bueno de este enfoque es que no cargará la matriz en la memoria ya que las entradas serán contadas por un iterador a medida que la API subyacente (Sistema de archivos) las lea.

Finalmente, para la información, conceptualmente en un sistema de archivos, un nodo de directorio no es necesario para contener el número de archivos que contiene, solo puede contener la lista de sus nodos secundarios (lista de inodos). No soy un experto en sistemas de archivos, pero creo que los sistemas de archivos UNIX funcionan así. Por lo tanto, no puede suponer que hay una forma de tener esta información directamente (es decir: siempre puede haber una lista de nodos secundarios ocultos en algún lugar).

Desafortunadamente, creo que ya es la mejor manera (aunque list() es ligeramente mejor que listFiles() , ya que no construye objetos File ).

Dado que realmente no necesita el número total, y de hecho desea realizar una acción después de un cierto número (en su caso 5000), puede usar java.nio.file.Files.newDirectoryStream . El beneficio es que puede salir temprano en lugar de tener que pasar por todo el directorio solo para obtener un conteo.

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

El documento de interfaz para DirectoryStream también tiene algunos buenos ejemplos.

Si tiene directorios que contienen realmente (> 100’000) muchos archivos, aquí hay una manera (no portátil) de ir:

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

Usar sigar debería ayudar. Sigar tiene ganchos nativos para obtener las estadísticas

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

Desafortunadamente, como dijo mmyers, File.list () es lo más rápido que va a obtener el uso de Java. Si la velocidad es tan importante como dices, puedes considerar realizar esta operación en particular usando JNI . A continuación, puede adaptar su código a su situación particular y sistema de archivos.

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