java - example - ¿Debo almacenar en búfer el InputStream o el InputStreamReader?
inputstream java 8 (4)
¿Cuáles son las diferencias (si las hay) entre los siguientes dos enfoques de almacenamiento en búfer?
Reader r1 = new BufferedReader(new InputStreamReader(in, "UTF-8"), bufferSize);
Reader r2 = new InputStreamReader(new BufferedInputStream(in, bufferSize), "UTF-8");
En respuesta a la pregunta de Ross Studtman en el comentario anterior (pero también relevante para el OP):
BufferedReader reader = new BufferedReader(new InputStreamReader(new BufferedInputSream(inputStream), "UTF-8"));
El BufferedInputStream
es superfluo (y probablemente perjudica el rendimiento debido a una copia extraña). Esto se debe a que BufferedReader
solicita caracteres de InputStreamReader
en grandes porciones llamando a InputStreamReader.read(char[], int, int)
, que a su vez (mediante StreamDecoder
) llama a InputStream.read(byte[], int, int)
un gran bloque de bytes del InputStream
subyacente.
Puede convencerse de que esto es así ejecutando el siguiente código:
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
Verás la siguiente salida:
InputStreamReader.read(..., 0, 8192)
ByteArrayInputStream.read(..., 0, 8192)
Esto demuestra que BufferedReader
solicita una gran parte de los caracteres de InputStreamReader
, que a su vez solicita una gran parte de los bytes de la InputStream
subyacente.
FWIW, si está abriendo un archivo en Java 8, puede usar Files.newBufferedReader(Path) . No sé cómo se compara el rendimiento con las otras soluciones que se describen aquí, pero al menos impulsa la decisión de qué construcción se debe almacenar en el JDK.
r1
es más eficiente. El InputStreamReader
sí no tiene un búfer grande. El BufferedReader
se puede configurar para tener un búfer más grande que el InputStreamReader
. El InputStreamReader
en r2
actuaría como un cuello de botella.
En una tuerca: debes leer los datos a través de un embudo, no a través de una botella.
Actualización : aquí hay un pequeño programa de referencia, solo tienes que copiar y pegar. No necesitas preparar archivos.
package com..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());
}
}
Resultados en mi Latitude E5500 con un disco duro 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.
r1
también es más conveniente cuando lee secuencias basadas en líneas ya que BufferedReader
admite el método readLine
. No tiene que leer el contenido en un búfer de matriz de caracteres o caracteres uno por uno. Sin embargo, tiene que convertir r1
en BufferedReader
o usar ese tipo explícitamente para la variable.
A menudo utilizo este fragmento de código:
BufferedReader br = ...
String line;
while((line=br.readLine())!=null) {
//process line
}