雖然使用谷歌搜索,但我發現使用java.io.File#length()
可能會很慢。 FileChannel
有一個size()
方法,也可用。java有效獲取文件大小
在java中有沒有一種有效的方法來獲取文件大小?
雖然使用谷歌搜索,但我發現使用java.io.File#length()
可能會很慢。 FileChannel
有一個size()
方法,也可用。java有效獲取文件大小
在java中有沒有一種有效的方法來獲取文件大小?
好,我試圖用下面的代碼來測量它:
對於運行= 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;
}
}
當我修改代碼以使用由絕對路徑,而不是資源訪問的文件,我得到不同的結果(1點運行,1次迭代和10萬字節的文件 - 次爲一10字節的文件是相同的100,000字節)
LENGTH總和:33,每次迭代:33.0
CHANNEL總和:3626,每次迭代:3626.0
URL總數:294,迭代次數:294.0
除了獲取長度以外,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));
}
}
}
針對rgrig的基準,開/所花費的時間關閉FileChannel & RandomAccessFile的情況下還需要考慮,因爲這些類將打開一個流讀取文件。
修改基準後,我得到了這些結果爲1次迭代上85MB的文件:
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";
}
}
}
其實,我認爲「ls」可能會更快。 Java在處理獲取文件信息時肯定存在一些問題。不幸的是,沒有用於Windows的遞歸ls的等價安全方法。 (cmd.exe的DIR/S可能會感到困惑,並在無限循環中產生錯誤)
在XP上,訪問局域網上的服務器,需要5秒鐘才能得到文件夾中的文件數(33,000 )和總大小。
當我在Java中通過這個遞歸迭代時,它花費了我5分鐘以上。我開始測量file.length(),file.lastModified()和file.toURI()所花的時間,我發現我的時間有99%是由這3次調用拍攝的。我實際上需要做的3個調用...
1000個文件的區別是15ms本地和1800ms服務器。 Java中的服務器路徑掃描速度非常慢。如果本機操作系統可以快速掃描相同的文件夾,爲什麼不能Java?
作爲一個更完整的測試,我使用XP上的WineMerge來比較服務器上的文件與本地文件的修改日期和大小。這是遍歷每個文件夾中33,000個文件的整個目錄樹。總時間7秒。 java:超過5分鐘。
所以來自OP的原始聲明和問題是真實的和有效的。在處理本地文件系統時不太明顯。在WinMerge中進行33,000個文件夾的本地比較需要3秒鐘,而在Java中需要32秒。所以再次,Java與原生是這些基本測試的10倍放緩。
爪哇1.6.0_22(最新),千兆LAN,和網絡連接,平是小於1ms(均在相同的開關)
Java是緩慢的。
這篇文章中的所有測試用例都存在缺陷,因爲他們訪問每個測試方法的相同文件。因此,測試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
我遇到了同樣的問題。我需要在網絡共享上獲得90,000個文件的文件大小和修改日期。使用Java,儘可能簡潔,需要很長時間。 (我需要從文件中獲取URL以及對象的路徑,所以它有所不同,但超過了一個小時)。然後,我使用了本機Win32可執行文件,並執行了相同的任務,只是轉儲文件路徑,修改和大小,並從Java執行。速度非常驚人。本地進程和我的字符串處理來讀取數據可以每秒處理超過1000個項目。
所以即使人們對上述評論進行排名,這是一個有效的解決方案,並且解決了我的問題。在我的情況下,我知道需要提前大小的文件夾,並且我可以將它通過命令行傳遞給我的win32應用程序。我從幾個小時開始處理一個目錄到幾分鐘。
該問題似乎也是Windows特定的。OS X沒有相同的問題,並且可以像操作系統那樣快速地訪問網絡文件信息。
Java在Windows上進行文件處理非常糟糕。本地磁盤訪問文件雖然很好。這只是網絡共享造成了糟糕的表現。 Windows可以獲取網絡共享信息並在一分鐘內計算總大小。
--Ben
從GHAD的基準,有幾個問題人提到:
1>像BalusC提到:stream.available()在這種情況下流動。
由於available()返回的估計值可以從此輸入流中讀取(或跳過)的字節數,而不會因下一次調用此輸入流的方法而被阻塞。
所以首先刪除這個URL的方法。
2>正如StuartH所述 - 測試運行的順序也會導致高速緩存不同,因此請單獨運行測試以取得測試結果。
現在開始測試:
當CHANNEL一個單獨運行:
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();
}
如果您想要目錄中多個文件的文件大小,請使用Files.walkFileTree
。您可以從BasicFileAttributes
獲取您將收到的尺寸。
這對於File.listFiles()
的結果調用.length()
或對Files.newDirectoryStream()
的結果使用Files.size()
要快得多。在我的測試案例中,它大約快了100倍。
你能否提供鏈接說File.length()「可以很慢」? – 2008-09-22 19:02:37
對不起,這裏是鏈接 http://www.javaperformancetuning.com/tips/rawtips.shtml 搜索 「文件信息,如File.length()需要系統調用,可能會很慢。」 這真是一個令人困惑的陳述,似乎幾乎認爲這將是一個系統調用。 – joshjdevl 2008-09-22 19:53:25
無論您如何操作,獲取文件長度都需要系統調用。如果它通過網絡或其他非常慢的文件系統,速度可能會很慢。沒有比File.length()更快的方法來獲取它,而這裏「慢」的定義只是意味着不要不必要地調用它。 – jsight 2008-09-22 20:18:12