我应该缓冲InputStream还是InputStreamReader?

时间:2010-08-11 14:04:19

标签: java buffering java-io bufferedreader bufferedinputstream

以下两种缓冲方法之间有什么区别(如果有的话)?

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

4 个答案:

答案 0 :(得分:28)

r1效率更高。 InputStreamReader本身没有大缓冲区。可以将BufferedReader设置为具有比InputStreamReader更大的缓冲区。 InputStreamReader中的r2会成为瓶颈。

坚果:你应该通过漏斗读取数据,而不是通过瓶子。


更新:这是一个小基准程序,只是复制'n'paste'n'run它。您无需准备文件。

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());
    }

}

我的Latitude 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.

答案 1 :(得分:5)

当您阅读基于行的流时,

r1也更方便BufferedReader支持readLine方法。您不必将内容逐个读取到char数组缓冲区或chars中。但是,您必须将r1转换为BufferedReader或明确使用该类型作为变量。

我经常使用此代码段:

BufferedReader br = ...
String line;
while((line=br.readLine())!=null) {
  //process line
}

答案 2 :(得分:1)

回应Ross Studtman在上述评论中提出的问题(但也与OP有关):

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

这表明InputStreamReaderInputStream请求大量字符,而{{1}}又从底层{{1}}请求大块字节。

答案 3 :(得分:1)

FWIW,如果您在Java 8中打开文件,则可以使用Files.newBufferedReader(Path)。我不知道性能与这里描述的其他解决方案相比如何,但至少它推动了什么构造缓冲到JDK的决定。

相关问题