2010-08-11 21 views

Respuesta

26

r1 es más eficiente. El InputStreamReader en sí mismo no tiene un gran buffer. El BufferedReader se puede configurar para tener un búfer más grande que InputStreamReader. El InputStreamReader en r2 actuaría como un cuello de botella.

En una tuerca: debe 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, al igual que copy'n'paste'n'run. No necesita preparar archivos.

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

} 

Resultados a 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. 
+0

Si el InputStream subyacente fuera un FileInputStream, ¿los dos lectores realizarían diferentes cantidades de lecturas de disco durante todo un proceso de lectura? – bdkosher

+0

Lo revisé usando perfmon, no veo diferencias notables.Actualizaré la respuesta pronto para incluir un fragmento de código de referencia. – BalusC

+1

Grande como para el nombre del paquete :) –

5

r1 también es más conveniente cuando se lee la corriente basada en la línea que soporta BufferedReaderreadLine método. No tiene que leer el contenido en un búfer de matriz char o caracteres uno por uno. Sin embargo, debe convertir r1 en BufferedReader o usar ese tipo explícitamente para la variable.

que a menudo utilizan este fragmento de código:

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

En respuesta a la pregunta de Ross Studtman en el comentario anterior (pero también es 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 la copia extraña). Esto se debe a la BufferedReader pide personajes de la InputStreamReader en grandes trozos llamando InputStreamReader.read(char[], int, int), que a su vez (a través de StreamDecoder) llama InputStream.read(byte[], int, int) para leer un gran bloque de bytes desde el 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á el siguiente resultado:

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

Esto demuestra que los BufferedReader peticiones de una gran parte de los personajes del InputStreamReader, que a su vez solicita un gran fragmento de bytes del InputStream subyacente.

+0

Y si usa el 'BufferedInputStream' solicita datos del' InputStream' en trozos grandes, y satisface las solicitudes más pequeñas de 'Readers' fuera de su buffer. No es 'superfluo'. – EJP

+0

@EJP: El 'BufferedInputStream' en mi fragmento de ejemplo (primer bloque de código en mi respuesta) es superfluo porque' BufferedReader' solicita bloques grandes del 'InputStreamReader', que a su vez solicita bloques grandes del' InputStream' subyacente. La inserción de un 'BufferedInputStream' entre el' InputStreamReader' y el 'InputStream' subyacente simplemente agrega sobrecarga sin comprar ninguna ganancia de rendimiento. –

1

FWIW, si está abriendo un archivo en Java 8, puede usar el Files.newBufferedReader(Path). No sé cómo se compara el rendimiento con las otras soluciones descritas aquí, pero al menos impulsa la decisión de qué construcción almacenar en el JDK.

Cuestiones relacionadas