1. 概要
このチュートリアルでは、Java を使用して大きなファイルを効率的に読み取る方法を説明します。この記事は、Baeldung (http://www.baeldung.com/) の「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 クラスを使用して、ファイルの内容をスキャンし、それを 1 行ずつ連続的に読み取ります:
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(); } }
このソリューションは、ファイルをすべて走査します。 rows in - 各行への参照を維持せずに各行を処理できるようにします。つまり、メモリには保存されません: (約 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 ストリーム
ライブラリが提供するカスタム LineIterator を使用して、Commons IO ライブラリを使用して実装することもできます:
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