2009-03-26 12 views
48

Come si conta il numero di file in una directory utilizzando Java? Per semplicità, assumiamo che la directory non abbia alcuna sottodirectory.Conteggio del numero di file in una directory utilizzando Java

So che il metodo standard di:

new File(<directory path>).listFiles().length 

ma questo sarà effettivamente passare attraverso tutti i file nella directory, che potrebbe richiedere molto tempo, se il numero di file è di grandi dimensioni. Inoltre, non mi importa dei file effettivi nella directory a meno che il loro numero sia maggiore di un numero fisso elevato (ad esempio 5000).

Sto indovinando, ma la directory (o il suo i-node in caso di Unix) non memorizza il numero di file in esso contenuti? Se potessi ottenere quel numero immediatamente dal file system, sarebbe molto più veloce. Devo eseguire questo controllo per ogni richiesta HTTP su un server Tomcat prima che il back-end inizi a eseguire l'elaborazione vera e propria. Pertanto, la velocità è di fondamentale importanza.

Potrei eseguire un daemon di tanto in tanto per cancellare la directory. Lo so, quindi per favore non darmi quella soluzione.

+0

Se la directory potenzialmente ha un numero enorme di file (1000 +), è possibile evitare di allocare l'array restituito dai metodi dell'elenco File. Non ho ancora provato questo, ma forse potresti usare listFiles e passargli un'istanza FileFilter che esegue il conteggio dei file nel metodo accept, mentre allo stesso tempo restituisce false per tutti i file. Suppongo che questo eviti l'allocazione dell'array, pur continuando a fornire un conteggio dei file. –

+0

Ignora il mio ultimo commento ... A seconda del JDK impl, l'array può essere assegnato comunque (sotto il cofano).Ciò sembra essere il caso in openjdk comunque. –

+0

Per Java 7 e versioni successive, questo problema ha una buona soluzione con un'API Java standard. Vedi la risposta di @ mateuscb di seguito: http://stackoverflow.com/questions/687444/contenuto-il-numero-di-file-in-un-directory-utilizzando-java/30784016#30784016. –

risposta

9

Questo potrebbe non essere appropriato per l'applicazione, ma è sempre possibile provare una chiamata nativa (utilizzando jni o jna) oppure eseguire un comando specifico della piattaforma e leggere l'output prima di tornare a list(). Length. Su * nix, è possibile eseguire ls -1a | wc -l (nota: si tratta di dash-one-a per il primo comando e dash-minuscolo-L per il secondo). Non sai cosa sarebbe giusto su Windows - forse solo a dir e cerca il sommario.

Prima di preoccuparsi di qualcosa di simile, ti consiglio vivamente di creare una directory con un numero molto elevato di file e solo vedere se list(). Length richiede davvero troppo tempo. Come suggerisce this blogger, potresti non voler sudare questo.

Probabilmente andrei con la risposta di Varkhan me stesso.

+1

È '-a' appropriato nel caso della soluzione' ls'? Non elencerebbe anche '.' e' ..'? –

+0

E penso che si possa volere un '-f' se ci sono molti file nella directory, altrimenti la maggior parte del tempo verrà speso facendo l'ordinamento predefinito. – Glenn

15

Sfortunatamente, credo che sia già il modo migliore (anche se list() è leggermente migliore di listFiles(), poiché non crea oggetti File).

67

Ah ... la logica per non avere un metodo diretto in Java per farlo è l'astrazione dello storage di file: alcuni filesystem potrebbero non avere il numero di file in una directory facilmente disponibile ... il conteggio potrebbe anche non avere significato (vedi per esempio filesystem distribuiti, P2P, fs che memorizzano gli elenchi di file come un elenco collegato, o filesystem supportati da database ...). Quindi sì,

new File(<directory path>).list().length 

è probabilmente la soluzione migliore.

+0

IMO, che non giustifica l'assenza di tale metodo - potrebbe semplicemente restituire null per FS dove N/A. Le FS esotiche non sono una ragione per sprecare cicli nell'ottenere un array. –

+0

Questo non ha senso per me. Perché puoi ottenere tutti i file e contarli ma non semplicemente ottenere il conteggio? Dov'è la differenza? –

1

Sfortunatamente, come ha detto mmyers, File.list() è tanto veloce quanto l'utilizzo di Java. Se la velocità è importante come dici tu, potresti prendere in considerazione l'idea di fare questa particolare operazione usando JNI. È quindi possibile adattare il codice alla situazione e al filesystem specifici.

3

Se si dispone di directory che contengono veramente (> 100'000) molti file, ecco un modo (non portatile) per andare:

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(); 
reader.close(); 
System.out.println(fileCount); 
2

Utilizzando Sigar dovrebbe aiutare.Sigar ha ganci nativo per ottenere le statistiche

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

Prestazioni? Memoria? Overhead? – Antares42

5

Dal momento che non si ha realmente bisogno il numero totale, e di fatto vogliono eseguire un'azione dopo un certo numero (nel tuo caso 5000), è possibile utilizzare java.nio.file.Files.newDirectoryStream. Il vantaggio è che puoi uscire presto, invece di dover passare attraverso l'intera directory solo per ottenere un conteggio.

public boolean isOverMax(){ 
    Path dir = Paths.get("C:/foo/bar"); 
    int i = 1; 

    try (DirectoryStream<Path> 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; 
} 

Il interface doc per DirectoryStream ha anche alcuni buoni esempi.

23

Dal Java 8, si può fare in una sola riga:

Files.list(Paths.get("your/path/here")).count(); 

Per quanto riguarda i 5000 nodi figlio e aspetti inode:

Questo metodo iterare le voci, ma come suggerito Varkhan probabilmente si può Puoi fare di più oltre a giocare con JNI o ​​chiamate dirette ai comandi di sistema, ma anche in questo caso non puoi mai essere sicuro che questi metodi non facciano la stessa cosa!

Tuttavia, cerchiamo di scavare in questo un po ':

Guardando alla fonte JDK8, Files.list espone una flusso che utilizza un Iterable da Files.newDirectoryStream che i delegati al FileSystemProvider.newDirectoryStream.

Su sistemi UNIX (decompilato sun.nio.fs.UnixFileSystemProvider.class), carica un iteratore: viene utilizzato un sun.nio.fs.UnixSecureDirectoryStream (con blocchi di file durante l'iterazione attraverso la directory).

Quindi, c'è un iteratore che eseguirà il loop delle voci qui.

Ora, diamo un'occhiata al meccanismo di conteggio.

Il conteggio effettivo viene eseguito dall'API di riduzione del conteggio/somma esposto da Java 8 streams. In teoria, questa API può eseguire operazioni parallele senza troppi sforzi (con multihtreading). Tuttavia il flusso viene creato con parallelismo disabilitato quindi è no va ...

Il lato buono di questo approccio è che non caricherà la matrice nella memoria quando i dati vengono contate da un iteratore come vengono letti dall'API (Filesystem) sottostante.

Infine, per le informazioni, concettualmente in un file system, un nodo di directory non è richiesto il possesso della numero dei file in esso contenuti, si può solo contenere l'elenco dei è nodi figlio (elenco di inode). Non sono un esperto di filesystem, ma credo che i filesystem UNIX funzionino proprio così. Quindi non puoi presumere che ci sia un modo per avere queste informazioni direttamente (cioè: ci può sempre essere una lista di nodi figli nascosti da qualche parte).

+2

Java 8 'Files.list()' genera 'IOException'; il metodo 'list()' della classe 'File' non genera eccezioni. –

+0

Sto usando '' Files.list() '' per una directory con 1-2 milioni di file, e ovviamente ci vuole un po '. Ma ho la sensazione che questo sia dietro alcune eccezioni di overhead GC che ho incontrato, perché milioni di oggetti file vengono istanziati e distrutti per ogni chiamata. Ancora alla ricerca di un metodo performante e sicuro per la memoria ... – Antares42

Problemi correlati