Подтвердить что ты не робот

Зачем использовать BufferedInputStream для чтения байта файла байтом быстрее, чем с помощью FileInputStream?

Я пытался прочитать файл в массиве с помощью FileInputStream, а файл размером ~ 800 КБ занял около 3 секунд для чтения в память. Затем я попробовал тот же код, кроме как с FileInputStream, завернутым в BufferedInputStream, и потребовалось около 76 миллисекунд. Почему чтение байта файла байтом выполняется намного быстрее с помощью BufferedInputStream, хотя я все еще читаю его байтом байтом? Здесь код (остальная часть кода совершенно неактуальна). Обратите внимание, что это "быстрый" код. Вы можете просто удалить BufferedInputStream, если хотите "медленный" код:

InputStream is = null;

    try {
        is = new BufferedInputStream(new FileInputStream(file));

        int[] fileArr = new int[(int) file.length()];

        for (int i = 0, temp = 0; (temp = is.read()) != -1; i++) {
            fileArr[i] = temp;
        }

BufferedInputStream более чем в 30 раз быстрее. Это гораздо больше. Итак, почему это так, и возможно ли сделать этот код более эффективным (без использования каких-либо внешних библиотек)?

4b9b3361

Ответ 1

В FileInputStream метод read() читает один байт. Из исходного кода:

/**
 * Reads a byte of data from this input stream. This method blocks
 * if no input is yet available.
 *
 * @return     the next byte of data, or <code>-1</code> if the end of the
 *             file is reached.
 * @exception  IOException  if an I/O error occurs.
 */
public native int read() throws IOException;

Это естественный вызов ОС, который использует диск для чтения одного байта. Это тяжелая операция.

С помощью BufferedInputStream метод делегирует перегруженный метод read(), который считывает количество байтов 8192 и буферизирует их до тех пор, пока они не понадобятся. Он по-прежнему возвращает только один байт (но сохраняет остальных в резерве). Таким образом, BufferedInputStream делает менее естественные вызовы ОС для чтения из файла.

Например, ваш файл длиной 32768 bytes. Чтобы получить все байты в памяти с помощью FileInputStream, вам потребуются собственные вызовы 32768 для ОС. При использовании BufferedInputStream вам потребуется только 4, независимо от количества вызовов read(), которые вы будете делать (все еще 32768).

Что касается того, как сделать это быстрее, вы можете рассмотреть класс Java 7 NIO FileChannel, но у меня нет никаких доказательств, подтверждающих это.

Ответ 2

BufferedInputStream, обернутый вокруг FileInputStream, будет запрашивать данные из FileInputStream в больших кусках (по-моему, по умолчанию 512 байтов). Таким образом, если вы читаете 1000 символов по одному, FileInputStream должен будет перейти только к диск дважды. Это будет намного быстрее!

Ответ 3

FileReader

FileReader предназначен для чтения потоков символов.

BufferedReader

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

В соответствии с документами FileReader делает запрос для каждой операции чтения. Это дорого.
Где BufferedReader делает запрос, когда его буфер заполняется.

Согласно документам

В общем, каждый запрос на чтение, сделанный из Reader, вызывает запрос на чтение, который должен быть сделан из базового символа или байтового потока. Поэтому целесообразно обернуть BufferedReader вокруг любого Reader, чьи операции read() могут быть дорогостоящими, например FileReaders и InputStreamReaders.

Прочтите это http://oopweb.com/Java/Documents/JavaNotes/Volume/chap84/ch84_3.html

Ответ 4

Это из-за стоимости доступа к диску. Допустим, у вас будет файл размером 8kb. Для чтения этого файла без BufferedInputStream потребуется 8 * 1024 раза доступ к диску.

В этот момент BufferedStream выходит на сцену и действует как средний человек между FileInputStream и файлом, который нужно прочитать.

В один снимок, получат куски байтов, по умолчанию 8kb в память, а затем FileInputStream будет читать байты от этого среднего человека. Это уменьшит время выполнения операции.

private void exercise1WithBufferedStream() {
      long start= System.currentTimeMillis();
        try (FileInputStream myFile = new FileInputStream("anyFile.txt")) {
            BufferedInputStream bufferedInputStream = new BufferedInputStream(myFile);
            boolean eof = false;
            while (!eof) {
                int inByteValue = bufferedInputStream.read();
                if (inByteValue == -1) eof = true;
            }
        } catch (IOException e) {
            System.out.println("Could not read the stream...");
            e.printStackTrace();
        }
        System.out.println("time passed with buffered:" + (System.currentTimeMillis()-start));
    }


    private void exercise1() {
        long start= System.currentTimeMillis();
        try (FileInputStream myFile = new FileInputStream("anyFile.txt")) {
            boolean eof = false;
            while (!eof) {
                int inByteValue = myFile.read();
                if (inByteValue == -1) eof = true;
            }
        } catch (IOException e) {
            System.out.println("Could not read the stream...");
            e.printStackTrace();
        }
        System.out.println("time passed without buffered:" + (System.currentTimeMillis()-start));
    }