Como ler um arquivo grande com eficiência com Java

Como ler um arquivo grande com eficiência com Java

1. Visão geral

Este tutorial mostraráhow to read all the lines from a large file in Java de maneira eficiente.

Este artigo faz parte dethe “Java – Back to Basic” tutorial aqui no exemplo.

Leitura adicional:

Java - Escreva um InputStream em um arquivo

Como gravar um InputStream em um arquivo - usando Java, Guava e a biblioteca do Commons IO.

Read more

Java - Converter arquivo para InputStream

Como abrir um InputStream a partir de um arquivo Java - usando Java simples, Guava e a biblioteca Apache Commons IO.

Read more

Java - Ler do arquivo

Leia o conteúdo de um arquivo em Java - usando qualquer um destes: BufferedReader, Scanner, StreamTokenizer, DataInputStream, SequenceInputStream, FileChannel, etc.

Read more

2. Leitura na memória

A maneira padrão de ler as linhas do arquivo está na memória - o Guava e o Apache Commons IO fornecem uma maneira rápida de fazer exatamente isso:

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

O problema com essa abordagem é que todas as linhas do arquivo são mantidas na memória - o que levará rapidamente aOutOfMemoryError se o arquivo for grande o suficiente.

Por exemplo -reading a ~1Gb file:

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

Isso começa com uma pequena quantidade de memória sendo consumida:(~0 Mb consumed)

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

Porém,after the full file has been processed, temos no final:(~2 Gb consumed)

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

O que significa que cerca de 2,1 Gb de memória são consumidos pelo processo - o motivo é simples - as linhas do arquivo estão sendo armazenadas na memória agora.

Deve ser óbvio neste ponto quekeeping in memory the contents of the file will quickly exhaust the available memory - independentemente de quanto isso realmente seja.

Além do mais,we usually don’t need all of the lines in the file in memory at once - em vez disso, só precisamos ser capazes de iterar em cada um, fazer algum processamento e jogá-lo fora. Então, isso é exatamente o que vamos fazer - iterar através das linhas sem manter na memória.

3. Streaming por meio do arquivo

Vamos agora olhar para uma solução - vamos usar umjava.util.Scanner para percorrer o conteúdo do arquivo e recuperar as linhas em série, uma por uma:

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

Esta solução irá iterar por todas as linhas do arquivo - permitindo o processamento de cada linha - sem manter referências a elas - e em conclusão,without 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 com Apache Commons IO

O mesmo pode ser obtido usando a biblioteca Commons IO, usandothe custom LineIterator fornecido pela biblioteca:

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

Como o arquivo inteiro não está totalmente na memória, isso também resultará empretty 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. Conclusão

Este artigo rápido mostra comoprocess lines in a large file without iteratively, without exhausting the available memory - o que é bastante útil ao trabalhar com esses arquivos grandes.

A implementação de todos esses exemplos e trechos de códigocan be found in our GitHub project - este é um projeto baseado em Maven, portanto, deve ser fácil de importar e executar como está.