2010-08-11 16 views
22

Quali sono le differenze (se presenti) tra i seguenti due approcci di buffering?Devo bufferizzare InputStream o InputStreamReader?

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

risposta

26

r1 è più efficiente. Lo stesso InputStreamReader non ha un buffer di grandi dimensioni. BufferedReader può essere impostato per avere un buffer più grande di InputStreamReader. Lo InputStreamReader in r2 fungerà da collo di bottiglia.

In un dado: è necessario leggere i dati attraverso un imbuto, non attraverso una bottiglia.


Aggiornamento: ecco un piccolo programma di benchmark, basta copy'n'paste'n'run esso. Non è necessario preparare i file.

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

} 

I risultati a mio Latitude E5500 con 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. 
+0

Se l'InputStream sottostante era un FileInputStream, i due Lettori eseguivano quantità diverse di letture del disco durante l'intero processo di lettura? – bdkosher

+0

L'ho controllato usando perfmon, non vedo differenze evidenti.Aggiornerò presto la risposta per includere uno snippet di codice di riferimento. – BalusC

+1

Grande come per il nome del pacchetto :) –

5

r1 è anche più conveniente quando si legge flusso line-based come BufferedReader supporta readLine metodo. Non è necessario leggere il contenuto in un buffer di array di caratteri o caratteri uno alla volta. Tuttavia, devi trasmettere r1 a BufferedReader oppure utilizzare quel tipo esplicitamente per la variabile.

Io uso spesso questo frammento di codice:

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

In risposta alla domanda di Ross Studtman nel commento di cui sopra (ma rilevanti anche per il PO):

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

Il BufferedInputStream è superfluo (e probabilmente danneggia le prestazioni a causa di copie estranee). Questo perché BufferedReader richiede caratteri dallo InputStreamReader in blocchi di grandi dimensioni chiamando InputStreamReader.read(char[], int, int), che a sua volta (tramite StreamDecoder) chiama InputStream.read(byte[], int, int) per leggere un grande blocco di byte dal sottostante InputStream.

ci si può convincere che questo è così eseguendo il seguente codice:

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 

si vedrà il seguente output:

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

Ciò dimostra che i BufferedReader richieste un grande pezzo di caratteri da InputStreamReader, che a sua volta richiede una grande quantità di byte dal sottostante InputStream.

+0

E se si utilizza il 'BufferedInputStream' richiede dati da' InputStream' in blocchi di grandi dimensioni e si suppone che le richieste più piccole dei 'Readers' siano fuori dal suo buffer. Non è "superfluo". – EJP

+0

@EJP: Il 'BufferedInputStream' nel mio snippet di esempio (il primo blocco di codice nella mia risposta) è superfluo perché' BufferedReader' richiede grossi blocchi da 'InputStreamReader', che a sua volta richiede grandi blocchi dal' InputStream' sottostante. L'inserimento di un 'BufferedInputStream' tra' InputStreamReader' e il sottostante 'InputStream' semplicemente aggiunge un overhead senza acquistare alcun guadagno in termini di prestazioni. –

1

FWIW, se si sta aprendo un filein Java 8, è possibile utilizzare il Files.newBufferedReader(Path). Non so come le prestazioni si confrontino con le altre soluzioni descritte qui, ma almeno spinge la decisione di quale costrutto da bufferizzare nel JDK.