Должен ли я буферизовать InputStream или InputStreamReader? - PullRequest
26 голосов
/ 11 августа 2010

Каковы различия (если таковые имеются) между следующими двумя подходами буферизации?

Reader r1 = new BufferedReader(new InputStreamReader(in, "UTF-8"), bufferSize);
Reader r2 = new InputStreamReader(new BufferedInputStream(in, bufferSize), "UTF-8");

Ответы [ 4 ]

28 голосов
/ 11 августа 2010

r1 более эффективно. Сам по себе InputStreamReader не имеет большого буфера. BufferedReader может иметь больший буфер, чем InputStreamReader. InputStreamReader в r2 будет узким местом.

В орехе: вы должны читать данные через воронку, а не через бутылку.


Обновление : вот небольшая тестовая программа, просто скопируйте и запустите ее. Вам не нужно готовить файлы.

package com.stackoverflow.q3459127;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

public class Test {

    public static void main(String... args) throws Exception {

        // Init.
        int bufferSize = 10240; // 10KB.
        int fileSize = 100 * 1024 * 1024; // 100MB.
        File file = new File("/temp.txt");

        // Create file (it's also a good JVM warmup).
        System.out.print("Creating file .. ");
        BufferedWriter writer = null;
        try {
            writer = new BufferedWriter(new FileWriter(file));
            for (int i = 0; i < fileSize; i++) {
                writer.write("0");
            }
            System.out.printf("finished, file size: %d MB.%n", file.length() / 1024 / 1024);
        } finally {
            if (writer != null) try { writer.close(); } catch (IOException ignore) {}
        }

        // Read through funnel.
        System.out.print("Reading through funnel .. ");
        Reader r1 = null;        
        try {
            r1 = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"), bufferSize);
            long st = System.nanoTime();
            for (int data; (data = r1.read()) > -1;);
            long et = System.nanoTime();
            System.out.printf("finished in %d ms.%n", (et - st) / 1000000);
        } finally {
            if (r1 != null) try { r1.close(); } catch (IOException ignore) {}
        }

        // Read through bottle.
        System.out.print("Reading through bottle .. ");
        Reader r2 = null;        
        try {
            r2 = new InputStreamReader(new BufferedInputStream(new FileInputStream(file), bufferSize), "UTF-8");
            long st = System.nanoTime();
            for (int data; (data = r2.read()) > -1;);
            long et = System.nanoTime();
            System.out.printf("finished in %d ms.%n", (et - st) / 1000000);
        } finally {
            if (r2 != null) try { r2.close(); } catch (IOException ignore) {}
        }

        // Cleanup.
        if (!file.delete()) System.err.printf("Oops, failed to delete %s. Cleanup yourself.%n", file.getAbsolutePath());
    }

}

Результаты на моей Локаторе E5500 с жестким диском Seagate Momentus 7200.3 :

Creating file .. finished, file size: 99 MB.
Reading through funnel .. finished in 1593 ms.
Reading through bottle .. finished in 7760 ms.
5 голосов
/ 11 августа 2010

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

Я часто использую этот фрагмент кода:

BufferedReader br = ...
String line;
while((line=br.readLine())!=null) {
  //process line
}
1 голос
/ 02 октября 2015

FWIW, если вы открываете файл в Java 8, вы можете использовать Files.newBufferedReader (Path) .Я не знаю, как производительность сравнивается с другими решениями, описанными здесь, но, по крайней мере, это подталкивает решение о том, какую конструкцию буферизировать в JDK.

1 голос
/ 07 декабря 2014

В ответ на вопрос Росса Студтмана в комментарии выше (но также относящийся к ФП):

BufferedReader reader = new BufferedReader(new InputStreamReader(new BufferedInputSream(inputStream), "UTF-8"));

BufferedInputStream является лишним (и, вероятно, вредит производительности из-за постороннего копирования). Это связано с тем, что BufferedReader запрашивает символы из InputStreamReader большими кусками, вызывая InputStreamReader.read(char[], int, int), который, в свою очередь (через StreamDecoder), вызывает InputStream.read(byte[], int, int) для чтения большого блока байтов из базового InputStream.

Вы можете убедиться в этом, запустив следующий код:

new BufferedReader(new InputStreamReader(new ByteArrayInputStream("Hello world!".getBytes("UTF-8")) {

    @Override
    public synchronized int read() {
        System.err.println("ByteArrayInputStream.read()");
        return super.read();
    }

    @Override
    public synchronized int read(byte[] b, int off, int len) {
        System.err.println("ByteArrayInputStream.read(..., " + off + ", " + len + ')');
        return super.read(b, off, len);
    }

}, "UTF-8") {

    @Override
    public int read() throws IOException {
        System.err.println("InputStreamReader.read()");
        return super.read();
    }

    @Override
    public int read(char[] cbuf, int offset, int length) throws IOException {
        System.err.println("InputStreamReader.read(..., " + offset + ", " + length + ')');
        return super.read(cbuf, offset, length);
    }

}).read(); // read one character from the BufferedReader

Вы увидите следующий вывод:

InputStreamReader.read(..., 0, 8192)
ByteArrayInputStream.read(..., 0, 8192)

Это показывает, что BufferedReader запрашивает большой кусок символов из InputStreamReader, который, в свою очередь, запрашивает большой кусок байтов из базового InputStream.

...