谷歌搜索时,我发现使用java.io.File#length()
可能很慢.
FileChannel
有一个size()
方法也可用.
在java中有一种有效的方法来获取文件大小吗?
好吧,我试着用下面的代码测量它:
对于runs = 1和iterations = 1,URL方法最快,其次是channel.我跑了几次,暂停了大约10次.因此,对于一次性访问,使用URL是我能想到的最快方式:
LENGTH sum: 10626, per Iteration: 10626.0 CHANNEL sum: 5535, per Iteration: 5535.0 URL sum: 660, per Iteration: 660.0
对于runs = 5和iterations = 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; EnumMapdurations = new EnumMap (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 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
我确实运行了以下修改过的代码,作为参数给出了100MB文件的名称.
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"); HashMaptimes = new HashMap (); 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 entry : times.entrySet()) { System.out.println( entry.getKey() + " sum: " + 1e-3 * entry.getValue() + ", per Iteration: " + (1e-3 * entry.getValue() / runs / iterations)); } } }
本文中的所有测试用例都存在缺陷,因为它们为每个测试方法访问相同的文件.因此,磁盘缓存会在测试2和3中受益.为了证明我的观点,我采用GHAD提供的测试用例并更改了枚举的顺序,以下是结果.
看结果我认为File.length()真的是赢家.
测试顺序是输出的顺序.您甚至可以看到我的机器所执行的时间在执行之间有所不同,但File.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
CHANNEL总和:3626,每次迭代:3626.0
URL总和:294,每次迭代:294.0
响应rgrig的基准测试,还需要考虑打开/关闭FileChannel和RandomAccessFile实例所花费的时间,因为这些类将打开用于读取文件的流.
修改基准测试后,我在85MB文件上获得了1次迭代的结果:
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)
如果您只需要文件大小,file.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]; Maptimes = new HashMap (); 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 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没有相同的问题,可以像操作系统那样快速访问网络文件信息.
Windows上的Java文件处理非常糟糕.但是文件的本地磁盘访问很好.只是网络股票造成了可怕的表现.Windows可以获取网络共享信息,并在一分钟内计算总大小.
--Ben