So lesen Sie eine große Datei effizient mit Java

Effizientes Lesen einer großen Datei mit Java

1. Überblick

Dieses Tutorial zeigthow to read all the lines from a large file in Java auf effiziente Weise.

Dieser Artikel ist Teil vonthe “Java – Back to Basic” tutorial hier am Beispiel.

Weitere Lektüre:

Java - Schreiben Sie einen InputStream in eine Datei

So schreiben Sie einen InputStream in eine Datei - mit Java, Guava und der Commons IO-Bibliothek.

Read more

Java - Datei in InputStream konvertieren

So öffnen Sie einen InputStream aus einer Java-Datei: Verwenden Sie Java, Guava und die Apache Commons IO-Bibliothek.

Read more

Java - Aus Datei lesen

Liest Inhalte aus einer Datei in Java - mit einem der folgenden Befehle: BufferedReader, Scanner, StreamTokenizer, DataInputStream, SequenceInputStream, FileChannel usw.

Read more

2. In Erinnerung lesen

Die Standardmethode zum Lesen der Zeilen der Datei befindet sich im Speicher. Sowohl Guava als auch Apache Commons IO bieten eine schnelle Möglichkeit, genau das zu tun:

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

Das Problem bei diesem Ansatz ist, dass alle Dateizeilen im Speicher bleiben - was schnell zuOutOfMemoryError führt, wenn die Datei groß genug ist.

Zum Beispiel -reading a ~1Gb file:

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

Dies beginnt mit einem geringen Speicherbedarf:(~0 Mb consumed)

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

after the full file has been processed haben wir jedoch am Ende:(~2 Gb consumed)

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

Dies bedeutet, dass der Prozess ungefähr 2,1 GB Speicher belegt - der Grund ist einfach - die Zeilen der Datei werden jetzt alle im Speicher gespeichert.

An diesem Punkt sollte klar sein, dasskeeping in memory the contents of the file will quickly exhaust the available memory - unabhängig davon, wie viel das tatsächlich ist.

Außerdemwe usually don’t need all of the lines in the file in memory at once - stattdessen müssen wir nur in der Lage sein, jedes einzelne zu durchlaufen, etwas zu verarbeiten und es wegzuwerfen. Genau das werden wir also tun - durch die Zeilen iterieren, ohne das im Speicher zu halten.

3. Streaming durch die Datei

Schauen wir uns nun eine Lösung an: Wir werden einjava.util.Scanner verwenden, um den Inhalt der Datei zu durchlaufen und Zeilen nacheinander seriell abzurufen:

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

Diese Lösung durchläuft alle Zeilen in der Datei und ermöglicht die Verarbeitung jeder Zeile - ohne Verweise darauf - und abschließendwithout keeping them in memory:(~150 Mb consumed)

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

4. Streaming mit Apache Commons IO

Dasselbe kann auch mit der Commons IO-Bibliothek erreicht werden, indemthe custom LineIterator verwendet werden, die von der Bibliothek bereitgestellt werden:

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

Da sich die gesamte Datei nicht vollständig im Speicher befindet, führt dies auch zupretty conservative memory consumption numbers:(~150 Mb consumed)

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

5. Fazit

Dieser kurze Artikel zeigt, wie manprocess lines in a large file without iteratively, without exhausting the available memory macht - was sich bei der Arbeit mit diesen großen Dateien als sehr nützlich erweist.

Die Implementierung all dieser Beispiele und Codefragmentecan be found in our GitHub project - dies ist ein Maven-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.