Java 我应该缓冲 InputStream 还是 InputStreamReader?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3459127/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 00:58:04  来源:igfitidea点击:

Should I buffer the InputStream or the InputStreamReader?

javabufferingjava-iobufferedreaderbufferedinputstream

提问by bdkosher

What are the differences (if any) between the following two buffering approaches?

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

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

采纳答案by BalusC

r1is more efficient. The InputStreamReaderitself doesn't have a large buffer. The BufferedReadercan be set to have a larger buffer than InputStreamReader. The InputStreamReaderin r2would act as a bottleneck.

r1效率更高。在InputStreamReader本身不具备大的缓冲。所述BufferedReader可以被设置为具有比较大的缓冲区InputStreamReader。将InputStreamReaderr2将作为一个瓶颈。

In a nut: you should read the data through a funnel, not through a bottle.

简而言之:您应该通过漏斗而不是瓶子读取数据。



Update: here's a little benchmark program, just copy'n'paste'n'run it. You don't need to prepare files.

更新:这是一个小基准程序,只需复制'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());
    }

}

Results at my Latitude E5500 with a Seagate Momentus 7200.3harddisk:

使用Seagate Momentus 7200.3硬盘的Latitude E5500 的结果:

Creating file .. finished, file size: 99 MB.
Reading through funnel .. finished in 1593 ms.
Reading through bottle .. finished in 7760 ms.

回答by pcjuzer

r1is also more convenient when you read line-based stream as BufferedReadersupports readLinemethod. You don't have to read content into a char array buffer or chars one by one. However, you have to cast r1to BufferedReaderor use that type explicitly for the variable.

r1当您将基于行的流作为BufferedReader支持readLine方法读取时,也更方便。您不必将内容一一读入字符数组缓冲区或字符。但是,你要投r1BufferedReader或明确使用该类型的变量。

I often use this code snippet:

我经常使用这个代码片段:

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

回答by Matt Whitlock

In response to Ross Studtman's question in the comment above (but also relevant to the OP):

针对上述评论中罗斯·斯图曼 (Ross Studtman) 的问题(但也与 OP 相关):

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

The BufferedInputStreamis superfluous (and likely harms performance due to extraneous copying). This is because the BufferedReaderrequests characters from the InputStreamReaderin large chunks by calling InputStreamReader.read(char[], int, int), which in turn (through StreamDecoder) calls InputStream.read(byte[], int, int)to read a large block of bytes from the underlying InputStream.

BufferedInputStream是多余的(并且可能由于无关的复制而损害性能)。这是因为通过调用BufferedReaderInputStreamReader大块中请求字符InputStreamReader.read(char[], int, int),而后者又(通过StreamDecoder)调用InputStream.read(byte[], int, int)从底层读取大块字节InputStream

You can convince yourself that this is so by running the following code:

您可以通过运行以下代码来说服自己确实如此:

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

You will see the following output:

您将看到以下输出:

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

This demonstrates that the BufferedReaderrequests a large chunk of characters from the InputStreamReader, which in turn requests a large chunk of bytes from the underlying InputStream.

这表明从BufferedReader请求了大量字符InputStreamReader,而后者又从底层请求了大量字节InputStream

回答by Max

FWIW, if you're opening a filein Java 8, you can use the Files.newBufferedReader(Path). I don't know how the performance compares to the other solutions described here, but at least it pushes the decision of what construct to buffer into the JDK.

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