1、概述本教程將演示如何用Java高效地讀取大文件。2、在內(nèi)存中讀取讀取文件行的標準方式是在內(nèi)存中讀取,Guava 和Apache Commons IO都提供了如下所示快速讀取文件行的方法:Files.readLines(new?File(path),?Charsets.UTF_8);
FileUtils.readLines(new?File(path));這種方法帶來的問題是文件的所有行都被存放在內(nèi)存中,當(dāng)文件足夠大時很快就會導(dǎo)致程序拋出OutOfMemoryError?異常。例如:讀取一個大約1G的文件:@Test
public?void?givenUsingGuava_whenIteratingAFile_thenWorks()?throws?IOException?{
???String?path?=?...
???Files.readLines(new?File(path),?Charsets.UTF_8);
}這種方式開始時只占用很少的內(nèi)存:(大約消耗了0Mb內(nèi)存)[main]?INFO??org.baeldung.java.CoreJavaIoUnitTest?-?Total?Memory:?128?Mb
[main]?INFO??org.baeldung.java.CoreJavaIoUnitTest?-?Free?Memory:?116?Mb然而,當(dāng)文件全部讀到內(nèi)存中后,我們最后可以看到(大約消耗了2GB內(nèi)存):[main]?INFO??org.baeldung.java.CoreJavaIoUnitTest?-?Total?Memory:?2666?Mb
[main]?INFO??org.baeldung.java.CoreJavaIoUnitTest?-?Free?Memory:?490?Mb這意味這一過程大約耗費了2.1GB的內(nèi)存——原因很簡單:現(xiàn)在文件的所有行都被存儲在內(nèi)存中。把文件所有的內(nèi)容都放在內(nèi)存中很快會耗盡可用內(nèi)存——不論實際可用內(nèi)存有多大,這點是顯而易見的。此外,我們通常不需要把文件的所有行一次性地放入內(nèi)存中——相反,我們只需要遍歷文件的每一行,然后做相應(yīng)的處理,處理完之后把它扔掉。所以,這正是我們將要做的——通過行迭代,而不是把所有行都放在內(nèi)存中。3、文件流現(xiàn)在讓我們看下這種解決方案——我們將使用java.util.Scanner類掃描文件的內(nèi)容,一行一行連續(xù)地讀?。篎ileInputStream?inputStream?=?null;
Scanner?sc?=?null;
try?{
???inputStream?=?new?FileInputStream(path);
???sc?=?new?Scanner(inputStream,?"UTF-8");
???while?(sc.hasNextLine())?{
???????String?line?=?sc.nextLine();
???????//?System.out.println(line);
???}
???//?note?that?Scanner?suppresses?exceptions
???if?(sc.ioException()?!=?null)?{
???????throw?sc.ioException();
???}
}?finally?{
???if?(inputStream?!=?null)?{
???????inputStream.close();
???}
???if?(sc?!=?null)?{
???????sc.close();
???}
}這種方案將會遍歷文件中的所有行——允許對每一行進行處理,而不保持對它的引用??傊疀]有把它們存放在內(nèi)存中:(大約消耗了150MB內(nèi)存)[main]?INFO??org.baeldung.java.CoreJavaIoUnitTest?-?Total?Memory:?763?Mb
[main]?INFO??org.baeldung.java.CoreJavaIoUnitTest?-?Free?Memory:?605?Mb4、Apache Commons IO流同樣也可以使用Commons IO庫實現(xiàn),利用該庫提供的自定義LineIterator:LineIterator?it?=?FileUtils.lineIterator(theFile,?"UTF-8");
try?{
???while?(it.hasNext())?{
???????String?line?=?it.nextLine();
???????//?do?something?with?line
???}
}?finally?{
???LineIterator.closeQuietly(it);
}由于整個文件不是全部存放在內(nèi)存中,這也就導(dǎo)致相當(dāng)保守的內(nèi)存消耗:(大約消耗了150MB內(nèi)存)[main]?INFO??o.b.java.CoreJavaIoIntegrationTest?-?Total?Memory:?752?Mb
[main]?INFO??o.b.java.CoreJavaIoIntegrationTest?-?Free?Memory:?564?Mb5、結(jié)論這篇短文介紹了如何在不重復(fù)讀取與不耗盡內(nèi)存的情況下處理大文件——這為大文件的處理提供了一個有用的解決辦法。所有這些例子的實現(xiàn)和代碼片段都可以在我的github項目上獲取到——這是一個基于Eclipse的項目,所以它應(yīng)該很容易被導(dǎo)入和運行。原文地址:http://www.codeceo.com/article/java-read-big-file.html
Java讀取大文件的高效率實現(xiàn)
慕課網(wǎng)Java學(xué)習(xí)小組_0001
2015-01-27 10:18:05