在使用Google搜索时,我发现使用java.io.File#length()
可能会很慢。
FileChannel
也有一个size()
方法。
java中有一种有效的方法来获取文件大小吗?
答案 0 :(得分:99)
好吧,我试着用下面的代码测量它:
对于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;
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;
}
}
答案 1 :(得分:32)
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");
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 :(得分:17)
本文中的所有测试用例都存在缺陷,因为它们为每个测试方法访问相同的文件。因此,磁盘缓存会在测试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
答案 3 :(得分:9)
当我修改你的代码以使用由绝对路径而不是资源访问的文件时,我得到不同的结果(1次运行,1次迭代和100,000字节文件 - 10字节文件的时间相同到100,000字节)
长度总和:33,每次迭代:33.0
CHANNEL总和:3626,每次迭代:3626.0
网址:294,每次迭代:294.0
答案 4 :(得分:8)
响应rgrig的基准,打开/关闭FileChannel&amp;还需要考虑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];
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";
}
}
}
答案 5 :(得分:8)
我遇到了同样的问题。我需要在网络共享上获取90,000个文件的文件大小和修改日期。使用Java,尽可能简约,需要很长时间。 (我需要从文件中获取URL,以及对象的路径。所以它有所不同,但超过一个小时。)然后我使用本机Win32可执行文件,并执行相同的任务,只是转储文件路径,修改和控制台的大小,并从Java执行。速度惊人。本机进程和我读取数据的字符串处理可以每秒处理1000多个项目。
所以尽管人们对上述评论进行了评分,但这是一个有效的解决方案,并且确实解决了我的问题。在我的情况下,我知道我需要提前大小的文件夹,我可以在命令行中将其传递给我的win32应用程序。我花了几个小时来处理一个目录到几分钟。
这个问题似乎也是Windows特有的。 OS X没有相同的问题,可以像操作系统那样快速访问网络文件信息。
Windows上的Java文件处理非常糟糕。但是文件的本地磁盘访问很好。只是网络股票造成了可怕的表现。 Windows可以获取网络共享信息,并在一分钟内计算总大小。
- 本
答案 6 :(得分:3)
如果您想要目录中多个文件的文件大小,请使用Files.walkFileTree
。您可以从您收到的BasicFileAttributes
获取尺寸。
这比.length()
的结果File.listFiles()
或Files.size()
的结果Files.newDirectoryStream()
更快。在我的测试用例中它快了大约100倍。
答案 7 :(得分:2)
实际上,我认为“ls”可能会更快。在处理获取文件信息的Java中肯定存在一些问题。不幸的是,Windows没有等效的递归ls安全方法。 (cmd.exe的DIR / S可能会混淆并在无限循环中生成错误)
在XP上,访问局域网上的服务器,在Windows中需要5秒才能获得文件夹中文件的数量(33,000)以及总大小。
当我在Java中以递归方式迭代时,它花了我超过5分钟。我开始测量执行file.length(),file.lastModified()和file.toURI()所花费的时间,我发现这些调用占用了99%的时间。我真正需要做的3个电话......
1000个文件的差异是15ms本地与服务器上的1800ms。 Java中的服务器路径扫描速度非常慢。如果本机操作系统可以快速扫描同一个文件夹,为什么不能用Java?
作为一个更完整的测试,我在XP上使用WineMerge比较修改日期,服务器上文件的大小与本地文件的大小。这是在每个文件夹中的33,000个文件的整个目录树上进行迭代。总时间,7秒。 java:超过5分钟。
所以来自OP的原始陈述和问题是真实的,有效的。处理本地文件系统时不太明显。在WinMerge中对文件夹与33,000项进行本地比较需要3秒,在Java中本地需要32秒。因此,在这些基本测试中,java与native相比减少了10倍。
Java 1.6.0_22(最新),千兆网卡和网络连接,ping不到1毫秒(都在同一个交换机中)
Java很慢。
答案 8 :(得分:2)
从GHad的基准测试中,人们提到了一些问题:
1&gt;与BalusC提到的一样:在这种情况下流动了stream.available()。
因为available()返回估计可以从此输入流中读取(或跳过)的字节数,而不会被下一次调用此输入流的方法阻塞。< / p>
首先删除此方法的URL。
2&gt;正如StuartH所提到的 - 测试运行的顺序也会使缓存产生差异,所以请单独运行测试。
现在开始测试:
当CHANNEL一人独自运行时:
CHANNEL sum: 59691, per Iteration: 238.764
当LENGTH独自运行时:
LENGTH sum: 48268, per Iteration: 193.072
所以看起来LENGTH就是胜利者:
@Override
public long getResult() throws Exception {
File me = new File(FileSizeBench.class.getResource(
"FileSizeBench.class").getFile());
return me.length();
}