BufferedReader Java: эффективное чтение файлов в Java

Эффективная работа с текстовыми файлами - одна из важнейших задач при разработке приложений на Java. В этой статье мы подробно рассмотрим как ускорить чтение файлов в Java с помощью класса BufferedReader.

Обзор BufferedReader в Java

BufferedReader - это класс в стандартной библиотеке Java, предназначенный для эффективного посимвольного чтения потока символов. Он реализует буферизацию ввода, что позволяет существенно ускорить чтение из медленных источников данных вроде файлов, сетевых сокетов и т.д.

Основные преимущества использования BufferedReader:

  • Высокая скорость чтения за счет использования внутреннего буфера
  • Удобные методы для построчного чтения текстовых файлов
  • Возможность чтения из любого источника, реализующего интерфейс Reader

По сравнению с обычным FileReader, BufferedReader позволяет увеличить скорость чтения файлов в несколько раз за счет сокращения числа обращений к диску.

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

Руки печатающие на ноутбуке с кофе рядом

Создание экземпляра BufferedReader

Для работы с BufferedReader нужно создать его экземпляр, передав в конструктор другой объект Reader для чтения базового потока:

FileReader fileReader = new FileReader("data.txt");
BufferedReader reader = new BufferedReader(fileReader);

Здесь в качестве источника данных для буферизации используется FileReader. Но на самом деле можно использовать любой другой Reader, например InputStreamReader для чтения из сокета или ByteArrayInputStream.

Помимо основного конструктора, в BufferedReader есть еще один, который позволяет явно указать размер буфера в байтах:

 BufferedReader reader = new BufferedReader(fileReader, 1024); 

По умолчанию используется буфер в 8 Кбайт, но для некоторых задач может потребоваться его настройка. Например, при чтении больших файлов имеет смысл увеличить размер буфера до 16-64 Кбайт.

Чтение данных с помощью BufferedReader

Для чтения данных из потока в BufferedReader предоставляется 3 основных метода:

  • read() - читает один символ
  • read(char[] buffer) - читает блок символов в указанный массив
  • readLine() - читает одну строку текста

Рассмотрим их использование на примерах.

Чтение файла посимвольно с помощью read():

 int c; while((c = reader.read()) != -1) { System.out.print((char)c); } 

Здесь мы читаем символы в цикле, пока не будет достигнут конец потока (он помечается возвратом -1).

А вот пример чтения в массив:

 char[] buf = new char[1024]; int charsRead = reader.read(buf); 

В переменной charsRead будет число реально прочитанных символов.

Наконец, читаем файл по строкам:

 String line; while((line = reader.readLine()) != null) { System.out.println(line); } 

Метод readLine() автоматически читает все символы до конца строки или конца файла.

Рабочий стол программиста с ноутбуком

Закрытие потока BufferedReader

После того, как мы закончили чтение данных с помощью BufferedReader, важно не забыть закрыть используемый поток с помощью метода close():

 reader.close(); 

Это позволит освободить системные ресурсы, выделенные под поток. Кроме того, для автоматического закрытия потока удобно использовать конструкцию try-with-resources:

 try(BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) { // ... работа с читателем } 

В этом случае поток будет закрыт автоматически по завершении блока try.

Дополнительные возможности BufferedReader

Кроме основных методов чтения, BufferedReader предоставляет и некоторые дополнительные возможности. Рассмотрим некоторые из них.

Чтение из разных источников

Одно из главных преимуществ BufferedReader в том, что он позволяет читать данные из любого источника, реализующего интерфейс Reader. Это может быть файл, сетевой поток, консоль или что угодно еще.

Например, реализуем буферизацию при чтении из сокета:

 Socket socket = new Socket("example.com", 80); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); 

А вот пример чтения из консоли:

 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String line = reader.readLine(); // ждем ввода строки 

Сравнение производительности

Чтобы наглядно увидеть выигрыш в производительности от использования BufferedReader, можно реализовать чтение одного и того же файла с ним и без него и сравнить скорость.

Например:

 // Чтение с помощью FileReader long start = System.currentTimeMillis(); // читаем файл long end = System.currentTimeMillis(); System.out.println("Время чтения с FileReader: " + (end - start) + "мс"); // Чтение с помощью BufferedReader start = System.currentTimeMillis(); // читаем файл end = System.currentTimeMillis(); System.out.println("Время чтения с BufferedReader: " + (end - start) + "мс"); 

Для больших файлов разница может быть в несколько раз!

BufferedReader против Scanner

Еще одним распространенным способом чтения текстовых данных в Java является класс Scanner. В чем же разница между ним и BufferedReader?

Основные отличия:

  • Scanner умеет парсить примитивные типы, а BufferedReader только строки
  • У Scanner фиксированный размер буфера, а в BufferedReader можно настроить
  • BufferedReader немного быстрее за счет того, что не парсит данные
  • BufferedReader потокобезопасный, Scanner - нет

В целом, для простого построчного чтения текста BufferedReader подходит лучше. А Scanner удобен, когда нужен парсинг входных данных.

Лучшие практики использования BufferedReader

В заключение дадим несколько полезных советов по использованию BufferedReader.

Выбор размера буфера

Как уже упоминалось, по умолчанию используется буфер 8Кб. Но для оптимальной производительности он может потребовать настройки. Например:

  • Для чтения с сети/консоли лучше буфер поменьше, 256-2048 байт
  • Для чтения больших файлов с диска буфер побольше, 16-64Кб

Многопоточное чтение

Поскольку BufferedReader потокобезопасный, его можно смело использовать для чтения из одного потока в нескольких потоках одновременно без дополнительной синхронизации.

Обработка исключений

Методы BufferedReader могут выбрасывать исключения IOException, поэтому их нужно оборачивать в try/catch.

Либо проще использовать уже упомянутый try-with-resources, чтобы не писать try/catch вручную.

Интеграция со Stream API

Можно использовать BufferedReader вместе со Stream API, с помощью метода lines():

 BufferedReader reader = Files.newBufferedReader(path); long count = reader.lines().count(); 

Это позволит применять все преимущества Stream API при чтении текстовых файлов.

Статья закончилась. Вопросы остались?
Комментарии 0
Подписаться
Я хочу получать
Правила публикации
Редактирование комментария возможно в течении пяти минут после его создания, либо до момента появления ответа на данный комментарий.