Научитесь читать все строки из большого файла(размером в ГБ) в Java и избегайте любых проблем с производительностью, таких как очень высокое использование памяти или даже OutOfMemoryError, если файл достаточно большой.
1. Подход к чтению больших файлов
Подобно парсеру DOM и парсеру SAX для XML-файлов, мы можем прочитать файл двумя способами:
- Чтение всего файла в памяти перед его обработкой
- Чтение содержимого файла построчно и независимая обработка каждой строки
Первый подход выглядит чище и подходит для небольших файлов, где требования к памяти очень низкие(в килобайтах или нескольких мегабайтах). Если использовать его для чтения больших файлов, он быстро приведет к OutOfMemoryError для файлов размером в гигабайты.
Второй подход подходит для чтения очень больших файлов в гигабайтах, когда нецелесообразно читать весь файл в память. В этом подходе мы используем потоковую передачу строк, т.е. читаем строки из файла в виде потока или итератора.
В этом руководстве основное внимание уделяется решениям, использующим второй подход.
2. Files.lines() – чтение большого файла в Java 8
При использовании метода Files.lines() содержимое файла считывается и обрабатывается лениво, так что в любой момент времени в памяти хранится только небольшая часть файла.
Преимущество этого подхода в том, что мы можем напрямую писать действия потребителя и использовать новые возможности языка, такие как лямбда-выражения с Stream.
Path filePath = Paths.get("C:/temp/file.txt")//try-with-resourcestry(Stream<String> lines = Files.lines( filePath )){lines.forEach(System.out::println);}catch(IOException e){e.printStackTrace();}
3. Общие операции ввода-вывода FileUtils.lineIterator()
lineIterator() использует Reader для итерации по строкам указанного файла. Используйте try-with-resources для автоматического закрытия итератора после чтения файла.
Не забудьте импортировать последнюю версию модуля commons-io в зависимости проекта.
<зависимость><groupId>commons-io</groupId><artifactId>commons-io</artifactId><версия>2.11.0</версия></зависимость>
File file = new File("C:/temp/file.txt");try(LineIterator it = FileUtils.lineIterator(file, "UTF-8")) {while(it.hasNext()) {String line = it.nextLine();// do something with lineSystem.out.println(line);}} catch(IOException e) {e.printStackTrace();}
4. Чтение большого двоичного файла
Обратите внимание, что когда мы читаем файлы в потоке или построчно, мы имеем в виду файлы на основе символов или текстовые файлы. При чтении двоичных файлов кодировка UTF-8 может повредить данные, поэтому указанное выше решение не применимо к двоичным файлам данных.
Для чтения больших файлов необработанных данных, таких как фильмы или большие изображения, мы можем использовать классы ByteBuffer и FileChannel Java NIO. Помните, что вам нужно будет попробовать разные размеры буфера и выбрать тот, который подходит вам лучше всего.
try(RandomAccessFile aFile = new RandomAccessFile("test.txt", "r");FileChannel inChannel = aFile.getChannel();) {//Buffer size is 1024ByteBuffer buffer = ByteBuffer.allocate(1024);while(inChannel.read(buffer) > 0) {buffer.flip();for(int i = 0; i < buffer.limit(); i++) {System.out.print((char) buffer.get());}buffer.clear(); // do something with the data and clear/compact it.}} catch(IOException e) {e.printStackTrace();}
5. Заключение
В этом руководстве по Java обсуждалось, какой класс следует использовать для эффективного чтения больших файлов. Правильное решение зависит от типа файла и других решающих факторов, характерных для данной проблемы.
Я предложу провести сравнительный анализ всех решений в вашей среде и выбрать их на основе их производительности.