Подсчет количества файлов в каталоге с помощью Java
Как подсчитать количество файлов в каталоге с помощью Java ? Для простоты предположим, что в каталоге нет никаких подкаталогов.
Я знаю стандартный метод:
new File(<directory path>).listFiles().length
но это будет эффективно проходить через все файлы в каталоге, что может занять много времени, если количество файлов велико. Кроме того, я не забочусь о фактических файлах в каталоге, если их количество не превышает некоторого фиксированного большого числа (скажем, 5000).
Я предполагаю, но разве каталог (или его i-узел в случае Unix) не хранит количество файлов, содержащихся в нем? Если бы я мог получить этот номер сразу из файловой системы, это было бы намного быстрее. Мне нужно сделать эту проверку для каждого HTTP-запроса на сервере Tomcat, прежде чем серверная часть начнет выполнять реальную обработку. Поэтому скорость имеет первостепенное значение.
Я мог бы запустить демон каждый раз в то время, чтобы очистить каталог. Я знаю это, так что, пожалуйста не давай мне это решение.
9 ответов:
Это может быть не подходит для вашего приложения, но вы всегда можете попробовать собственный вызов (используя jni или jna), или выполнить команду для конкретной платформы и прочитать выходные данные, прежде чем вернуться к списку ().длина. На *nix, вы можете exec
ls -1a | wc -l(Примечание-это тире-один-a для первой команды, и тире-нижний регистр-L для второй). Не уверен, что будет правильно на windows-возможно, простоdirи посмотрите на резюме.прежде чем беспокоиться о чем-то вроде этого я настоятельно рекомендую вам создать каталог с очень большим количеством файлов и просто посмотреть, если список().длина действительно занимает слишком много времени. Как этот блогер предполагает, что вы не можете потеть это.
Я бы, наверное, сам пошел с ответом Вархана.
Ах... причиной отсутствия простого метода в Java для этого является абстракция хранилища файлов: некоторые файловые системы могут не иметь количества файлов в каталоге, доступном для быстрого доступа... это число может даже не иметь никакого значения (см., например, распределенные файловые системы P2P, fs, которые хранят списки файлов в виде связанного списка или файловые системы с поддержкой базы данных...). Так что да,
new File(<directory path>).list().lengthвероятно, ваш лучший ставку.
начиная с 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 может выполнять параллельные операции без особых усилий (с многопоточностью). Однако поток создается с отключенным параллелизмом, поэтому он не работает...
The хорошей стороны этот подход заключается в том, что он не будет загружать массив в поскольку записи будут подсчитываться итератором по мере их чтения базовым API (файловой системы).
наконец, для информации, концептуально в файловой системе узел каталога не требуется для хранения файлов, которые он содержит, он может просто содержит список его дочерних узлов (список индексов). Я не эксперт по файловым системам, но я считаю, что файловые системы UNIX работают именно так. Поэтому вы не можете предположить, что есть способ получить эту информацию напрямую (т. е. всегда может быть какой-то список дочерних узлов, скрытых где-то).
к сожалению, я считаю, что это уже самый лучший способ (хотя
list()немного лучше, чемlistFiles(), так как он не построитьFileобъекты).
Так как вам действительно не нужно общее число, а на самом деле хотите выполнить действие после определенного числа (в вашем случае 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; }The интерфейс doc на
DirectoryStreamтакже есть несколько хороших примеров.
Если у вас есть каталоги, содержащие действительно (>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);
использование сигара должно помочь. сигар есть родные крючки, чтобы получить статистика
new Sigar().getDirStat(dir).getTotal()
к сожалению, как сказал mmyers, файл.list () примерно так же быстро, как вы собираетесь использовать Java. Если скорость так важна, как вы говорите, вы можете рассмотреть возможность выполнения этой конкретной операции с помощью JNI. Затем вы можете адаптировать свой код к конкретной ситуации и файловой системе.
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); }
Comments