Zliczanie ilości plików w katalogu za pomocą Javy

Jak policzyć liczbę plików w katalogu za pomocą Javy ? Dla uproszczenia, Załóżmy, że katalog nie ma żadnych podkatalogów.

Znam standardową metodę:

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

Ale to skutecznie przejdzie przez wszystkie pliki w katalogu, co może potrwać długo, jeśli liczba plików jest duża. Poza tym, nie dbam o rzeczywiste pliki w katalogu, chyba że ich liczba jest większa niż jakaś stała duża liczba (powiedzmy 5000).

Zgaduję, ale czy katalog (lub jego i-node w przypadku Uniksa) nie przechowuje ilości plików w nim zawartych? Gdybym mógł dostać ten numer od razu z systemu plików, byłoby znacznie szybciej. Muszę to sprawdzić dla każdego żądania HTTP na serwerze Tomcat, zanim back-end zacznie wykonywać prawdziwe przetwarzanie. Dlatego szybkość ma ogromne znaczenie.

Mógłbym uruchomić demona raz na jakiś czas, aby wyczyścić katalog. Wiem o tym, więc proszę, nie dawaj mi tego rozwiązania.

Author: Paŭlo Ebermann, 2009-03-26

9 answers

Może to nie być odpowiednie dla Twojej aplikacji, ale zawsze możesz spróbować natywnego wywołania (używając jni lub jna ) lub użyć polecenia specyficznego dla platformy i odczytać wyjście przed spadnięciem do list().długość. Na *nix, można exec ls -1a | wc -l (uwaga-to jest dash-one-a dla pierwszego polecenia, i dash-małe litery-L dla drugiego). Nie wiem, co by było dobre na windows-może po prostu dir i poszukaj podsumowania.

Zanim zadręczę się czymś takim, zdecydowanie zalecamy utworzenie katalogu z bardzo dużą liczbą plików i sprawdzenie czy list().długość naprawdę trwa zbyt długo. Jak sugeruje ten bloger, możesz nie chcieć tego pocić.

Pewnie sam bym wybrał odpowiedź Varkhana.
 10
Author: Marty Lamb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-20 11:57:39

Ah... powodem braku prostej metody w Javie jest abstrakcja przechowywania plików: niektóre systemy plików mogą nie mieć łatwo dostępnej liczby plików w katalogu... liczba ta może nawet nie mieć żadnego znaczenia (zobacz na przykład rozproszone, systemy plików P2P, systemy plików przechowujące listy plików jako listy połączone lub systemy plików wspierane przez bazy danych...). Więc tak,

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

To chyba najlepsza opcja.

 72
Author: Varkhan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-26 20:55:43

Od Javy 8 można to zrobić w trzech liniach:

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

W odniesieniu do 5000 węzłów potomnych i aspektów i-węzłów:

Ta metoda będzie iterować nad wpisami, ale jak zasugerował Varkhan, prawdopodobnie nie możesz zrobić lepiej poza grą z JNI lub bezpośrednimi komendami systemowymi, ale nawet wtedy, nigdy nie możesz być pewien, że te metody nie robią tego samego!

Jednak zagłębimy się w to trochę:

W przeciwieństwie do JDK8, w JDK8 nie ma żadnego źródła. Iterable from Files.newDirectoryStream that delegates to FileSystemProvider.newDirectoryStream.

W systemach uniksowych (dekompilowany sun.nio.fs.UnixFileSystemProvider.class) ładuje iterator: używany jest sun.nio.fs.UnixSecureDirectoryStream (z blokadą plików podczas iteracji przez katalog).

Istnieje więc iterator, który będzie przeszukiwał wpisy tutaj.

Przyjrzyjmy się mechanizmowi liczenia.

Rzeczywista liczba jest wykonywana przez API redukujące liczbę/sumę wystawione przez strumienie Java 8. Teoretycznie ten API może wykonywać operacje równoległe bez większego wysiłku (z multihtreading). Jednak strumień jest tworzony z wyłączoną równoległością, więc nie ma go...

Dobrą stroną tego podejścia jest to, że nie ładuje tablicy w pamięci, ponieważ wpisy będą liczone przez iterator, tak jak są odczytywane przez bazowe (system plików) API.

Wreszcie, dla informacji, koncepcyjnie w systemie plików, węzeł katalogowy nie jest wymagany do przechowywania numeru plików, które zawiera, Może po prostu zawiera listę węzłów potomnych (lista i-węzłów). Nie jestem ekspertem od systemów plików, ale wierzę, że systemy plików UNIX działają tak samo. Nie można więc zakładać, że istnieje sposób, aby mieć te informacje bezpośrednio (tzn.: zawsze może być gdzieś ukryta lista węzłów potomnych).

 23
Author: superbob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-22 08:58:04

Niestety, uważam, że jest to już najlepszy sposób (chociaż list() jest nieco lepszy niż listFiles(), ponieważ nie konstruuje File obiektów).

 15
Author: Michael Myers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-26 20:34:49

Ponieważ tak naprawdę nie potrzebujesz liczby całkowitej, a w rzeczywistości chcesz wykonać czynność po określonej liczbie( w Twoim przypadku 5000), możesz użyć java.nio.file.Files.newDirectoryStream. Zaletą jest to, że możesz wyjść wcześniej, zamiast przechodzić przez cały katalog, aby uzyskać licznik.

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

Interfejs doc dla DirectoryStream ma również kilka dobrych przykładów.

 5
Author: mateuscb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-24 20:04:37

Jeśli masz katalogi zawierające naprawdę (>100 ' 000) wiele plików, oto (nie-przenośny) sposób:

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);
 4
Author: Renaud,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-15 21:12:19

Używanie sigar powinno pomóc. Sigar ma natywne Hooki, aby uzyskać statystyki

new Sigar().getDirStat(dir).getTotal()
 2
Author: user2162827,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-12 14:32:44

Niestety, jak mmyers powiedział, plik.list() jest mniej więcej tak szybki, jak przy użyciu Javy. Jeśli prędkość jest tak ważna, jak mówisz, warto rozważyć wykonanie tej konkretnej operacji za pomocą JNI . Następnie możesz dostosować swój kod do konkretnej sytuacji i systemu plików.

 1
Author: Sebastian Celis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-26 20:39:55
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);
}
 0
Author: Sergii Povzaniuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-31 15:40:32