Как эффективно читать большие файлы с помощью Java

Как эффективно читать большие файлы с помощью Java

1. обзор

Этот учебник покажетhow to read all the lines from a large file in Java эффективным способом.

Эта статья является частьюthe “Java – Back to Basic” tutorial здесь для примера.

Дальнейшее чтение:

Java - запись InputStream в файл

Как записать InputStream в файл - используя Java, Guava и библиотеку ввода-вывода Commons.

Read more

Java - конвертировать файл в InputStream

Как открыть InputStream из файла Java - используя простую Java, Guava и библиотеку Apache Commons IO.

Read more

Java - чтение из файла

Чтение содержимого из файла в Java - с использованием любого из них: BufferedReader, Scanner, StreamTokenizer, DataInputStream, SequenceInputStream, FileChannel и т. Д.

Read more

2. Чтение в памяти

Стандартный способ чтения строк файла находится в памяти - и Guava, и Apache Commons IO предоставляют быстрый способ сделать это:

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

Проблема с этим подходом заключается в том, что все строки файла хранятся в памяти, что быстро приведет кOutOfMemoryError, если файл достаточно велик.

Например -reading a ~1Gb file:

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

Это начинается с использования небольшого количества памяти:(~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, в конце имеем:(~2 Gb consumed)

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

Это означает, что процесс потребляет около 2,1 ГБ памяти - причина проста - все строки файла теперь хранятся в памяти.

К этому моменту должно быть очевидно, чтоkeeping in memory the contents of the file will quickly exhaust the available memory - независимо от того, сколько это на самом деле.

Более того,we usually don’t need all of the lines in the file in memory at once - вместо этого нам просто нужно иметь возможность перебрать каждый из них, выполнить некоторую обработку и выбросить ее. Итак, это именно то, что мы собираемся делать - перебирать строки без сохранения в памяти.

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

Это решение будет перебирать все строки в файле, что позволяет обрабатывать каждую строку без сохранения ссылок на них, и в заключение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. Потоковая передача с Apache Commons IO

То же самое может быть достигнуто с помощью библиотеки Commons IO, используяthe custom LineIterator, предоставленные библиотекой:

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

Поскольку весь файл не полностью находится в памяти - это также приведет кpretty 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. Заключение

В этой быстрой статье показано, как использоватьprocess lines in a large file without iteratively, without exhausting the available memory - что оказывается весьма полезным при работе с этими большими файлами.

Реализация всех этих примеров и фрагментов кодаcan be found in our GitHub project - это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.