java получить размер файла эффективно
в то время как гуглить, я вижу, что с помощью java.io.File#length()
может быть медленным.
FileChannel
есть size()
метод, который также доступен.
есть ли эффективный способ в Java, чтобы получить размер файла?
9 ответов:
Ну, я попытался измерить его с помощью кода ниже:
для запусков = 1 и итераций = 1 метод URL является самым быстрым в большинстве случаев, за которым следует канал. Я запускаю это с некоторой паузой свежим около 10 раз. Поэтому для одноразового доступа, используя URL-адрес, это самый быстрый способ, который я могу придумать:
LENGTH sum: 10626, per Iteration: 10626.0 CHANNEL sum: 5535, per Iteration: 5535.0 URL sum: 660, per Iteration: 660.0
для прогонов = 5 и итераций = 50 рисунок рисует по-разному.
LENGTH sum: 39496, per Iteration: 157.984 CHANNEL sum: 74261, per Iteration: 297.044 URL sum: 95534, per Iteration: 382.136
файл должен кэшировать вызовы файловой системы, в то время как каналы и URL имеют некоторые накладные расходы.
код:
import java.io.*; import java.net.*; import java.util.*; public enum FileSizeBench { LENGTH { @Override public long getResult() throws Exception { File me = new File(FileSizeBench.class.getResource( "FileSizeBench.class").getFile()); return me.length(); } }, CHANNEL { @Override public long getResult() throws Exception { FileInputStream fis = null; try { File me = new File(FileSizeBench.class.getResource( "FileSizeBench.class").getFile()); fis = new FileInputStream(me); return fis.getChannel().size(); } finally { fis.close(); } } }, URL { @Override public long getResult() throws Exception { InputStream stream = null; try { URL url = FileSizeBench.class .getResource("FileSizeBench.class"); stream = url.openStream(); return stream.available(); } finally { stream.close(); } } }; public abstract long getResult() throws Exception; public static void main(String[] args) throws Exception { int runs = 5; int iterations = 50; EnumMap<FileSizeBench, Long> durations = new EnumMap<FileSizeBench, Long>(FileSizeBench.class); for (int i = 0; i < runs; i++) { for (FileSizeBench test : values()) { if (!durations.containsKey(test)) { durations.put(test, 0l); } long duration = testNow(test, iterations); durations.put(test, durations.get(test) + duration); // System.out.println(test + " took: " + duration + ", per iteration: " + ((double)duration / (double)iterations)); } } for (Map.Entry<FileSizeBench, Long> entry : durations.entrySet()) { System.out.println(); System.out.println(entry.getKey() + " sum: " + entry.getValue() + ", per Iteration: " + ((double)entry.getValue() / (double)(runs * iterations))); } } private static long testNow(FileSizeBench test, int iterations) throws Exception { long result = -1; long before = System.nanoTime(); for (int i = 0; i < iterations; i++) { if (result == -1) { result = test.getResult(); //System.out.println(result); } else if ((result = test.getResult()) != result) { throw new Exception("variance detected!"); } } return (System.nanoTime() - before) / 1000; } }
бенчмарк, заданный GHad, измеряет множество других вещей (таких как отражение, создание экземпляров объектов и т. д.) кроме того, длина. Если мы попытаемся избавиться от этих вещей, то за один звонок я получаю следующие времена в микросекундах:
file sum___19.0, per Iteration___19.0 raf sum___16.0, per Iteration___16.0 channel sum__273.0, per Iteration__273.0для 100 запусков и 10000 итераций я:
file sum__1767629.0, per Iteration__1.7676290000000001 raf sum___881284.0, per Iteration__0.8812840000000001 channel sum___414286.0, per Iteration__0.414286Я выполнил следующий измененный код, дающий в качестве аргумента имя файла размером 100 МБ.
import java.io.*; import java.nio.channels.*; import java.net.*; import java.util.*; public class FileSizeBench { private static File file; private static FileChannel channel; private static RandomAccessFile raf; public static void main(String[] args) throws Exception { int runs = 1; int iterations = 1; file = new File(args[0]); channel = new FileInputStream(args[0]).getChannel(); raf = new RandomAccessFile(args[0], "r"); HashMap<String, Double> times = new HashMap<String, Double>(); times.put("file", 0.0); times.put("channel", 0.0); times.put("raf", 0.0); long start; for (int i = 0; i < runs; ++i) { long l = file.length(); start = System.nanoTime(); for (int j = 0; j < iterations; ++j) if (l != file.length()) throw new Exception(); times.put("file", times.get("file") + System.nanoTime() - start); start = System.nanoTime(); for (int j = 0; j < iterations; ++j) if (l != channel.size()) throw new Exception(); times.put("channel", times.get("channel") + System.nanoTime() - start); start = System.nanoTime(); for (int j = 0; j < iterations; ++j) if (l != raf.length()) throw new Exception(); times.put("raf", times.get("raf") + System.nanoTime() - start); } for (Map.Entry<String, Double> entry : times.entrySet()) { System.out.println( entry.getKey() + " sum: " + 1e-3 * entry.getValue() + ", per Iteration: " + (1e-3 * entry.getValue() / runs / iterations)); } } }
все тестовые случаи в этом сообщении имеют недостатки, поскольку они обращаются к одному и тому же файлу для каждого проверенного метода. Так что дисковое кэширование пинает, в котором тесты 2 и 3 выигрывают. Чтобы доказать свою точку зрения, я взял тестовый случай, предоставленный GHAD, и изменил порядок перечисления, и ниже приведены результаты.
глядя на результат, я думаю, что файл.длина() является победителем на самом деле.
порядок тестирования-это порядок вывода. Вы даже можете видеть, что время, затраченное на мою машину, варьировалось между казнями, но Файл.Length() когда не первый, а первый доступ к диску выиграл.
--- LENGTH sum: 1163351, per Iteration: 4653.404 CHANNEL sum: 1094598, per Iteration: 4378.392 URL sum: 739691, per Iteration: 2958.764 --- CHANNEL sum: 845804, per Iteration: 3383.216 URL sum: 531334, per Iteration: 2125.336 LENGTH sum: 318413, per Iteration: 1273.652 --- URL sum: 137368, per Iteration: 549.472 LENGTH sum: 18677, per Iteration: 74.708 CHANNEL sum: 142125, per Iteration: 568.5
когда я изменяю ваш код, чтобы использовать файл, доступ к которому осуществляется по абсолютному пути вместо ресурса, я получаю другой результат (для 1 запуска, 1 итерации и 100 000 байтового файла-раз для 10-байтового файла идентичны 100 000 байтов)
длина сумма: 33, за итерацию: 33.0
сумма каналов: 3626, за итерацию: 3626.0
URL сумма: 294, за итерацию: 294.0
в ответ на тест rgrig также необходимо учитывать время, необходимое для открытия/закрытия экземпляров FileChannel & RandomAccessFile, так как эти классы откроют поток для чтения файла.
после изменения бенчмарка, я получил эти результаты для 1 итерации на 85 Мб файла:
file totalTime: 48000 (48 us) raf totalTime: 261000 (261 us) channel totalTime: 7020000 (7 ms)
для 10000 итераций в одном файле:
file totalTime: 80074000 (80 ms) raf totalTime: 295417000 (295 ms) channel totalTime: 368239000 (368 ms)
Если все, что вам нужно, это размер файла, файл.length () - это самый быстрый способ сделать это. Если вы планируете используйте файл для других целей, таких как чтение/запись, тогда RAF кажется лучшей ставкой. Только не забудьте закрыть файловое соединение : -)
import java.io.File; import java.io.FileInputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.util.HashMap; import java.util.Map; public class FileSizeBench { public static void main(String[] args) throws Exception { int iterations = 1; String fileEntry = args[0]; Map<String, Long> times = new HashMap<String, Long>(); times.put("file", 0L); times.put("channel", 0L); times.put("raf", 0L); long fileSize; long start; long end; File f1; FileChannel channel; RandomAccessFile raf; for (int i = 0; i < iterations; i++) { // file.length() start = System.nanoTime(); f1 = new File(fileEntry); fileSize = f1.length(); end = System.nanoTime(); times.put("file", times.get("file") + end - start); // channel.size() start = System.nanoTime(); channel = new FileInputStream(fileEntry).getChannel(); fileSize = channel.size(); channel.close(); end = System.nanoTime(); times.put("channel", times.get("channel") + end - start); // raf.length() start = System.nanoTime(); raf = new RandomAccessFile(fileEntry, "r"); fileSize = raf.length(); raf.close(); end = System.nanoTime(); times.put("raf", times.get("raf") + end - start); } for (Map.Entry<String, Long> entry : times.entrySet()) { System.out.println(entry.getKey() + " totalTime: " + entry.getValue() + " (" + getTime(entry.getValue()) + ")"); } } public static String getTime(Long timeTaken) { if (timeTaken < 1000) { return timeTaken + " ns"; } else if (timeTaken < (1000*1000)) { return timeTaken/1000 + " us"; } else { return timeTaken/(1000*1000) + " ms"; } } }
Я столкнулся с этой же проблемой. Мне нужно было получить размер файла и дату изменения 90 000 файлов в сетевом ресурсе. Используя Java и будучи максимально минималистичным, это займет очень много времени. (Мне нужно было получить URL-адрес из файла,а также путь к объекту. Так что его варьировали несколько, но больше часа.) Затем я использовал собственный исполняемый файл Win32 и выполнил ту же задачу, просто сбросив путь к файлу, измененный и размер на консоль и выполнил это с Java. Скорость это было потрясающе. Собственный процесс и моя обработка строк для чтения данных могут обрабатывать более 1000 элементов в секунду.
Так что, хотя люди вниз оценили выше комментарий, это правильное решение, и действительно решить мою проблему. В моем случае я знал папки, которые мне нужны были размеры заранее, и я мог передать это в командной строке моему приложению win32. Я пошел от часов каталог в минутах.
проблема также, похоже, специфична для Windows. OS X не было та же проблема и может получить доступ к информации о сетевом файле так же быстро, как это может сделать ОС.
обработка файлов Java в Windows ужасна. Локальный доступ к диску для файлов в порядке, хотя. Это были просто сетевые ресурсы, которые вызвали ужасную производительность. Windows может получить информацию о сетевом ресурсе и вычислить общий размер менее чем за минуту.
--Бен
если вы хотите размер файла нескольких файлов в каталоге, используйте
Files.walkFileTree
. Вы можете получить размер отBasicFileAttributes
что вы получите.это намного быстрее, чем вызов
.length()
в результатеFile.listFiles()
или черезFiles.size()
в результатеFiles.newDirectoryStream()
. В моих тестовых случаях это было примерно в 100 раз быстрее.
на самом деле, я думаю, что "LS" может быть быстрее. Есть определенно некоторые проблемы в Java, связанные с получением информации о файле. К сожалению, нет эквивалентного безопасного метода рекурсивного ls для Windows. (УМК.exe DIR / S может запутаться и генерировать ошибки в бесконечных циклах)
на XP, получая доступ к серверу в локальной сети, мне требуется 5 секунд в Windows, чтобы получить количество файлов в папке (33 000) и общий размер.
когда я повторяю рекурсивно через это в Java это занимает у меня более 5 минут. Я начал измерять время, необходимое для создания файла.длина(), файл.lastModified () и файл.тури () и я обнаружил, что 99% моего времени занимают эти 3 звонка. 3 звонка, которые мне действительно нужно сделать...
разница для 1000 файлов составляет 15 мс локально против 1800 МС на сервере. Сканирование пути сервера в Java происходит до смешного медленно. Если родная ОС может быть быстрой при сканировании той же папки, почему Java не может?
как более полный тест, я использовал WineMerge на XP для сравнения измененной даты и размера файлов на сервере по сравнению с файлами локально. Это повторялось по всему дереву каталогов из 33 000 файлов в каждой папке. Общее время, 7 секунд. java: более 5 минут.
таким образом, исходное утверждение и вопрос из OP истинны и действительны. Его менее заметно при работе с локальной файловой системой. Выполнение локального сравнения папки с 33 000 элементов занимает 3 секунды в WinMerge и занимает 32 секунд локально в Java. Так опять же, Ява по сравнению с родной 10-кратным замедлением эти элементарные тесты.
Java 1.6.0_22 (последняя версия), Gigabit LAN и сетевые подключения, ping меньше 1 мс (оба в одном коммутаторе)
Java-это медленно.
из бенчмарка GHad, есть несколько проблем, которые люди упомянули:
1 > Как упоминалось BalusC: поток.доступный () течет в этом случае.
потому что available () возвращает оценка числа байтов, которые могут быть считаны (или перескочиться) от этого входного потока, не блокируя следующий вызов метода для этого входного потока.
Итак, 1-й, чтобы удалить URL-адрес этого подхода.
2>Как упоминал Стюарт-the заказ тестового запуска также делает разницу в кэше, поэтому возьмите это, запустив тест отдельно.
Теперь начать тест:
когда Первый канал работает в одиночку:
CHANNEL sum: 59691, per Iteration: 238.764
когда длина один запуск в одиночку:
LENGTH sum: 48268, per Iteration: 193.072
Так выглядит Длина один победитель здесь:
@Override public long getResult() throws Exception { File me = new File(FileSizeBench.class.getResource( "FileSizeBench.class").getFile()); return me.length(); }