Страницы

Поиск по вопросам

вторник, 24 декабря 2019 г.

Зачем BufferedInputStream, если InputStream предоставляет read с буфером

#java #поток_ввода


Зачем понадобилось включать в систему ввода-вывода Java обертку BufferedInputStream,
если все реализации интерфейса InputStream по умолчанию содержат метод read(byte b[]),
в котором для чтения из потока используется буфер? 

Для примера, написал программу, которая копирует видеофайл ~100 мб из одного файла
в другой.


Реализация с использованием BufferedInputStream:

public static void main (String[] args) throws IOException{

BufferedInputStream fis=new BufferedInputStream(new FileInputStream("C:/tests/1.mp4"));
BufferedOutputStream fos=new BufferedOutputStream(new FileOutputStream("C:/tests/2.mp4"));
int b=fis.read();
while (b!=-1){

    fos.write(b);
    b=fis.read();

}
fos.flush();        


}
Реализация с использованием метода read(byte b[])

public static void main (String[] args) throws IOException{

FileInputStream fis=new FileInputStream("C:/tests/1.mp4");
FileOutputStream fos=new FileOutputStream("C:/tests/2.mp4");

byte[] buff=new byte[4096];
int bytes=fis.read(buff);
while(bytes!=-1){

    fos.write(buff, 0, bytes);
    bytes=fis.read(buff);

}


}


Реализация с использованием класса BufferedInputStream работает примерно в 10 раз
медленнее
    


Ответы

Ответ 1



Попробуйте сравнить скорость обработки с буферизацией и считыванием массива: BufferedInputStream fis = new BufferedInputStream(new FileInputStream("C:/tests/1.mp4")); BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("C:/tests/2.mp4")); byte[] buff = new byte[4096]; int bytes = fis.read(buff); while (bytes != -1) { fos.write(buff, 0, bytes); bytes = fis.read(buff); } fos.flush(); Полагаю, что скорость обработки будет сравнима со скоростью без буферизации, а может, даже быстрее, за счет другого размера буфера. Почему первый пример работает медленно Считывание/запись массива байтов из локального файла — очень быстрая операция. В лучше случае BufferedInputStream (BufferedOutputStream) не повлияет на производительность существенным образом, в худшем — производительность упадет из-за различных накладных расходов. В вашем случае байты (~ 10^8) после считывания из буфера обрабатываются по одному, что сильно увеличивает накладные расходы: для каждого байта вызываются методы read и write — вызовы методов не бесплатны; хуже того, оба метода синхронизированные (synchronized) и при каждом вызове виртуальная машина выполняет переключение контекста/блокировку; в самих методах есть разнообразные проверки (открыт ли входной/выходной поток, закончился ли буфер и т.п.), которые также отнимают время. Я полагаю, что основную нагрузку создает синхронизация. Можете для эксперимента добавить синхронизированные методы в вариант с FileInputStream while (bytes != -1) { for (int i = 0; i < bytes; i++) { write(read(buff[i])); } fos.write(buff, 0, bytes); bytes = fis.read(buff); } } static int count = 0; public static synchronized void write(int b) { //неважно count += b; } public static synchronized int read(int b) { return count + b; } Зачем BufferedInputStream Буферизированные потоки нужны чтобы не писать буферизацию самому. Также они позволяют отделить логику программы от настроек буферизации. BufferedInputStream особенно удобен если: Используется алгоритм, который обрабатывает байты последовательно. В этом случае можно упростить код, доверив буферизацию встроенному классу. Сам поток используется в качестве аргумента для класса-чтеца. Например, BufferedInputStream можно передать в качестве аргумента Scanner: Scanner scanner = new Scanner(new BufferedInputStream(new FileInputStream("input"))); После этого через Scanner можно будет работать с данными построчно, а BufferedInputStream сэкономит на обращениях к файловой системе. Используются специфичные методы. Методы InputStream.mark и InputStream.reset, как правило, недоступны в небуферизированных потоках. Соответственно, если потребуется откатывать состояние потока, то использовать FileInputStream не получится. На примере с копированием файла преимущества увидеть сложно. С другой стороны, для копирования файла целесообразнее использовать встроенные методы, а не потоки.

Комментариев нет:

Отправить комментарий