Подсчет количества файлов в каталоге с использованием Java - PullRequest
59 голосов
/ 26 марта 2009

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

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

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

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

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

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

Ответы [ 9 ]

79 голосов
/ 26 марта 2009

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

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

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

26 голосов
/ 12 января 2016

Начиная с 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.

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

16 голосов
/ 26 марта 2009

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

12 голосов
/ 27 марта 2009

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

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

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

6 голосов
/ 11 июня 2015

Поскольку вам на самом деле не нужно общее число, и вы действительно хотите выполнить действие после определенного числа (в вашем случае 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;
}

Интерфейс для интерфейса для DirectoryStream также имеет несколько хороших примеров.

4 голосов
/ 02 июля 2013

Если у вас есть каталоги, содержащие действительно (> 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);
2 голосов
/ 17 мая 2014

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

new Sigar().getDirStat(dir).getTotal()
1 голос
/ 31 мая 2018
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);
}
1 голос
/ 26 марта 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...