Автор оригинала: 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 основе проекта, поэтому она должна быть легко импортировать и работать, как она есть.