Java如何高效的讀取大檔案執行個體教程

來源:互聯網
上載者:User

       1、概述

  本教程將示範如何用Java高效地讀取大檔案,這些大檔案操作雖然不是很常用,但是如果有項目要用到的話就有用武之地了。

  2、在記憶體中讀取

  讀取檔案行的標準方式是在記憶體中讀取,Guava 和Apache Commons IO都提供了如下所示快速讀取檔案行的方法:

Files.readLines(new File(path), Charsets.UTF_8);
 
FileUtils.readLines(new File(path));

  這種方法帶來的問題是檔案的所有行都被存放在記憶體中,當檔案足夠大時很快就會導致程式拋出OutOfMemoryError 異常。

  例如:讀取一個大約1G的檔案:

@Test
public void givenUsingGuava_whenIteratingAFile_thenWorks() throws IOException {
    String path = ...
    Files.readLines(new File(path), Charsets.UTF_8);
}

  這種方式開始時只佔用很少的記憶體:(大約消耗了0Mb記憶體)

[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Total Memory: 128 Mb
[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Free Memory: 116 Mb

  然而,當檔案全部讀到記憶體中後,我們最後可以看到(大約消耗了2GB記憶體):

[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Total Memory: 2666 Mb
[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Free Memory: 490 Mb

  這意味這一過程大約耗費了2.1GB的記憶體——原因很簡單:現在檔案的所有行都被儲存在記憶體中。

  把檔案所有的內容都放在記憶體中很快會耗盡可用記憶體——不論實際可用記憶體有多大,這點是顯而易見的。

  此外,我們通常不需要把檔案的所有行一次性地放入記憶體中——相反,我們只需要遍曆檔案的每一行,然後做相應的處理,處理完之後把它扔掉。所以,這正是我們將要做的——通過行迭代,而不是把所有行都放在記憶體中。

  3、檔案流

  現在讓我們看下這種解決方案——我們將使用java.util.Scanner類掃描檔案的內容,一行一行連續地讀取:

FileInputStream 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();
    }
}

  這種方案將會遍曆檔案中的所有行——允許對每一行進行處理,而不保持對它的引用。總之沒有把它們存放在記憶體中:(大約消耗了150MB記憶體)

[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Total Memory: 763 Mb
[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Free Memory: 605 Mb

  4、Apache Commons IO流

  同樣也可以使用Commons IO庫實現,利用該庫提供的自訂LineIterator:

LineIterator it = FileUtils.lineIterator(theFile, "UTF-8");
try {
    while (it.hasNext()) {
        String line = it.nextLine();
        // do something with line
    }
} finally {
    LineIterator.closeQuietly(it);
}

  由於整個檔案不是全部存放在記憶體中,這也就導致相當保守的記憶體消耗:(大約消耗了150MB記憶體)

[main] INFO  o.b.java.CoreJavaIoIntegrationTest - Total Memory: 752 Mb
[main] INFO  o.b.java.CoreJavaIoIntegrationTest - Free Memory: 564 Mb

  5、結論

  這篇短文介紹了如何在不重複讀取與不耗盡記憶體的情況下處理大檔案——這為大檔案的處理提供了一個有用的解決辦法。

  所有這些例子的實現和程式碼片段都可以在我的github項目上擷取到——這是一個基於Eclipse的項目,所以它應該很容易被匯入和運行。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.