Каковы различия (если они есть) между следующими двумя подходами буферизации?
Reader r1 = new BufferedReader(new InputStreamReader(in, "UTF-8"), bufferSize);
Reader r2 = new InputStreamReader(new BufferedInputStream(in, bufferSize), "UTF-8");
Каковы различия (если они есть) между следующими двумя подходами буферизации?
Reader r1 = new BufferedReader(new InputStreamReader(in, "UTF-8"), bufferSize);
Reader r2 = new InputStreamReader(new BufferedInputStream(in, bufferSize), "UTF-8");
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 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 harddisk:
Creating file .. finished, file size: 99 MB. Reading through funnel .. finished in 1593 ms. Reading through bottle .. finished in 7760 ms.
r1
также удобнее, когда вы читаете поток на основе строки, поскольку BufferedReader
поддерживает метод readLine
. Вам не нужно читать содержимое в буфер массива char или символы один за другим. Однако вам нужно отбрасывать r1
в BufferedReader
или явно использовать этот тип для переменной.
Я часто использую этот фрагмент кода:
BufferedReader br = ...
String line;
while((line=br.readLine())!=null) {
//process line
}
FWIW, если вы открываете файл в Java 8, вы можете использовать Files.newBufferedReader(Path). Я не знаю, как производительность сравнивается с другими решениями, описанными здесь, но по крайней мере она подталкивает решение о том, какую конструкцию нужно буферизировать в JDK.
В ответ на вопрос Росса Штедмана в вышеприведенном комментарии (но также относящемся к 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
запрашивает большой фрагмент символов из InputStreamReader
, который, в свою очередь, запрашивает большую часть байтов из базового InputStream
.