Подсчет количества файлов в каталоге с помощью Java - программирование
Подтвердить что ты не робот

Подсчет количества файлов в каталоге с помощью Java

Как подсчитать количество файлов в каталоге с помощью Java? Для простоты предположим, что в каталоге нет подкаталогов.

Я знаю стандартный метод:

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

Но это будет эффективно проходить через все файлы в каталоге, что может занять много времени, если количество файлов велико. Кроме того, я не забочусь о фактических файлах в каталоге, если их число больше некоторого фиксированного большого числа (скажем, 5000).

Я предполагаю, но не каталог (или его i- node в случае Unix) хранит количество файлов, содержащихся в нем? Если бы я мог получить этот номер прямо из файловой системы, это было бы намного быстрее. Мне нужно сделать эту проверку для каждого HTTP-запроса на сервере Tomcat до того, как сервер начнет выполнять реальную обработку. Поэтому скорость имеет первостепенное значение.

Я мог запускать демона каждый раз, чтобы очистить каталог. Я знаю это, поэтому, пожалуйста, не давайте мне этого решения.

4b9b3361

Ответ 1

Это может быть неприемлемо для вашего приложения, но вы всегда можете попробовать собственный вызов (используя jni или jna) или exec определенную платформой команду и прочитайте вывод перед возвратом в list(). length. На * nix вы можете выполнить exec ls -1a | wc -l (обратите внимание - это черточка-один-a для первой команды, а нижний - нижний регистр-L для второго). Не уверен, что будет правильно на окнах - возможно, просто dir и найдите резюме.

Прежде чем беспокоиться о чем-то подобном, я настоятельно рекомендую вам создать каталог с очень большим количеством файлов и просто увидеть, если list(). длина действительно занимает слишком много времени. Как этот блоггер предлагает, вы можете не хотеть потеть.

Я, вероятно, поеду с Варханом на себя.

Ответ 2

А... обоснование отсутствия прямого метода в Java - это абстракция хранилища файлов: некоторые файловые системы могут не иметь количества файлов в каталоге, которые легко доступны... этот счет может даже не иметь никакого значения (см., например, распределенные файловые системы P2P, fs, которые хранят списки файлов в виде связанного списка или файловые системы с резервной копией базы данных...). Итак, да,

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

вероятно, лучший выбор.

Ответ 3

Начиная с Java 8, вы можете сделать это в трех строках:

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

Что касается 5000 дочерних узлов и аспектов inode:

Этот метод будет перебирать записи, но поскольку Вархан предположил, что вы, вероятно, не можете сделать лучше, кроме игры с JNI или прямыми вызовами системных команд, но даже тогда вы никогда не сможете быть уверены, что эти методы не делают то же самое!

Однако, позвольте вникнуть в это немного:

Глядя на источник JDK8, Files.list предоставляет поток, который использует Iterable из Files.newDirectoryStream который делегирует FileSystemProvider.newDirectoryStream.

В системах UNIX (sun.nio.fs.UnixFileSystemProvider.class) он загружает итератор: используется sun.nio.fs.UnixSecureDirectoryStream (с файловыми блокировками при повторении через каталог).

Итак, есть итератор, который будет прокручивать записи здесь.

Теперь посмотрим на счетный механизм.

Фактический подсчет выполняется с помощью API сокращения количества/суммы, отображаемого потоками Java 8. Теоретически, этот API может выполнять параллельные операции без особых усилий (с многоходовым). Однако поток создается с отключенным параллелизмом, поэтому он не идет...

Хорошей стороной этого подхода является то, что он не будет загружать массив в память, так как записи будут считаться итератором, поскольку они считываются базовым (файловым) API.

Наконец, для информации, концептуально в файловой системе, узел каталога не требуется для хранения количества файлов, которые он содержит, он может просто содержать список его дочерних узлов (список inodes). Я не эксперт в файловых системах, но я считаю, что файловые системы UNIX работают именно так. Таким образом, вы не можете предположить, что есть способ получить эту информацию напрямую (т.е. Всегда найдется какой-то список дочерних узлов).

Ответ 4

К сожалению, я считаю, что это лучший способ (хотя list() немного лучше, чем listFiles(), поскольку он не построить объекты File).

Ответ 5

Поскольку вам действительно не нужно общее число, и на самом деле вы хотите выполнить действие после определенного номера (в вашем случае 5000), вы можете использовать java.nio.file.Files.newDirectoryStream. Преимущество состоит в том, что вы можете выйти из него раньше, а затем пройти весь каталог, чтобы получить счет.

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

В документе doc для DirectoryStream также есть несколько хороших примеров.

Ответ 6

Если у вас есть каталоги, содержащие действительно (> 100'000) много файлов, вот (не переносимый) способ:

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

Ответ 7

Использование сигара должно помочь. Sigar имеет собственные крючки для получения статистики

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

Ответ 8

К сожалению, как сказал mmyers, File.list() примерно так же быстро, как вы собираетесь использовать Java. Если скорость важна, как вы говорите, вам может понадобиться рассмотреть эту конкретную операцию, используя JNI. Затем вы можете адаптировать свой код к конкретной ситуации и файловой системе.

Ответ 9

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