Должен ли я буфер ввода ввода или входную систему?
-
27-09-2019 - |
Вопрос
Каковы различия (если есть) между следующими двумя подходами буферизации?
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'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 7200.3 жесткий диск:
Создание файла .. Закончено, размер файла: 99 МБ. Чтение через воронку .. Закончено в 1593 мс. Чтение через бутылку .. Закончено в 7760 мс.
Другие советы
r1
также удобнее, когда вы читаете строку на основе потока как BufferedReader
поддерживает readLine
метод. Вам не нужно читать контент в буфер Char Array или Chars один за другим. Тем не менее, вы должны бросить r1
к BufferedReader
или использовать этот тип явно для переменной.
Я часто использую этот фрагмент кода:
BufferedReader br = ...
String line;
while((line=br.readLine())!=null) {
//process line
}
В ответ на вопрос 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
просит большой кусок персонажей из InputStreamReader
, который, в свою очередь, запрашивает большой кусок байтов из базового InputStream
.
FWIW, если вы открываете файл в Java 8 вы можете использовать Files.newbuftedReader (путь). Отказ Я не знаю, как производительность сравнивается с другими решениями, описанными здесь, но, по крайней мере, он толкает решение о том, что конструирует для буфера в JDK.