Рубрики
Без рубрики

Как эффективно читать большой файл с Java

Узнайте, как эффективно обрабатывать строки в большом файле с Помощью Java – не нужно хранить все в памяти.

Автор оригинала: Eugen Paraschiv.

1. Обзор

Этот учебник покажет как прочитать все строки из большого файла в Java эффективным образом.

Эта статья является частью “Java – Назад к основной” учебник здесь, на Baeldung.

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

Java — Напишите входной поток в файл

Java — Преобразование файла в inputStream

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

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

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

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

Например – чтение файла с 1 Гб :

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

Это начинается с небольшого количества памяти потребляется: (0 Мб потребляется)

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

Тем не менее, после обработки полного файла , у нас есть в конце: (2 Гб потребляется)

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

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

К этому моменту должно быть очевидно, что сохранение в памяти содержимого файла быстро исчерпает доступную память – независимо от того, сколько это на самом деле.

Более того, нам обычно не нужны все строки в файле в памяти сразу – Вместо этого, мы просто должны быть в состоянии итерировать через каждый из них, сделать некоторые обработки и выбросить его. Таким образом, это именно то, что мы собираемся сделать – итерировать через линии, не держа все из них в памяти.

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

Это решение будет итерировать через все строки в файле – позволяет для обработки каждой строки – без хранения ссылок на них – и в заключение, не сохраняя их в памяти : (150 Мб потребляется)

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

4. Потоковая передача с помощью Io Apache Commons

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

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

Так как весь файл не полностью в памяти – это также приведет к довольно консервативные цифры потребления памяти : (150 Мб потребляется)

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

5. Заключение

Эта быстрая статья показывает, как процесс линий в большом файле без итеративно, без исчерпания доступных – что оказывается весьма полезным при работе с этими большими файлами.

Реализация всех этих примеров и фрагментов кода можно найти в нашем Проект GitHub – это Maven основе проекта, поэтому она должна быть легко импортировать и работать, как она есть.