2010-08-11 20 views

Trả lời

26

r1 là hiệu quả hơn. Bản thân số InputStreamReader không có bộ đệm lớn. Bạn có thể đặt BufferedReader để có bộ đệm lớn hơn InputStreamReader. Các InputStreamReader trong r2 sẽ hoạt động như một nút cổ chai.

Trong đai ốc: bạn nên đọc dữ liệu qua kênh, chứ không phải qua chai.


Cập nhật: đây là một chương trình benchmark ít, chỉ copy'n'paste'n'run nó. Bạn không cần chuẩn bị tệp.

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

} 

Kết quả tại E5500 Latitude của tôi với một ổ cứng 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

Nếu InputStream cơ bản là một FileInputStream, hai người đọc có thực hiện các lượng đĩa khác nhau đọc trong suốt toàn bộ quá trình đọc không? – bdkosher

+0

Tôi đã kiểm tra nó bằng cách sử dụng perfmon, tôi không thấy sự khác biệt đáng chú ý.Tôi sẽ sớm cập nhật câu trả lời để bao gồm đoạn mã điểm chuẩn. – BalusC

+1

Giống như tên gói :) –

5

r1 cũng thuận tiện hơn khi bạn đọc dòng dựa trên dòng như BufferedReader hỗ trợ readLine phương pháp. Bạn không cần phải đọc nội dung vào bộ đệm mảng char hoặc từng ký tự một. Tuy nhiên, bạn phải truyền r1 đến BufferedReader hoặc sử dụng loại đó một cách rõ ràng cho biến.

Tôi thường sử dụng đoạn mã này:

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

Trả lời câu hỏi Ross Studtman trong những nhận xét ở trên (mà còn liên quan đến OP):

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

Các BufferedInputStream là không cần thiết (và có thể gây hại hiệu quả do sao chép không liên quan). Điều này là do các ký tự BufferedReader yêu cầu các ký tự từ InputStreamReader trong các khối lớn bằng cách gọi InputStreamReader.read(char[], int, int), lần lượt (thông qua StreamDecoder) gọi InputStream.read(byte[], int, int) để đọc một khối byte lớn từ số InputStream cơ bản.

Bạn có thể thuyết phục bản thân rằng đây là quá bằng cách chạy đoạn mã sau:

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 

Bạn sẽ thấy kết quả như sau:

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

Điều đó chứng tỏ các BufferedReader yêu cầu một mảng lớn của nhân vật từ InputStreamReader, do đó yêu cầu một đoạn lớn byte từ bên dưới InputStream.

+0

Và nếu bạn sử dụng 'BufferedInputStream', nó yêu cầu dữ liệu từ' InputStream' trong các khối lớn, và bổ sung các yêu cầu nhỏ hơn của 'Readers' ra khỏi bộ đệm của nó. Nó không phải là 'thừa'. – EJP

+0

@EJP: 'BufferedInputStream' trong đoạn mã mẫu của tôi (khối mã đầu tiên trong câu trả lời của tôi) là thừa vì' BufferedReader' yêu cầu các khối lớn từ 'InputStreamReader', do đó yêu cầu các khối lớn từ' InputStream' cơ bản. Việc chèn một 'BufferedInputStream' giữa' InputStreamReader' và phần tử 'InputStream' cơ bản chỉ thêm vào chi phí mà không cần mua bất kỳ hiệu suất nào. –

1

FWIW, nếu bạn đang mở một tệp trong Java 8, bạn có thể sử dụng Files.newBufferedReader(Path). Tôi không biết làm thế nào hiệu suất so sánh với các giải pháp khác được mô tả ở đây, nhưng ít nhất nó đẩy quyết định của những gì xây dựng để đệm vào JDK.