2013-08-25 41 views
6

如您所知,我們在java中有幾個用於將數據寫入流的工具。
在這個示例代碼中,我通過運行時比較了它們。
有人可以解釋它嗎?謝謝。
下面是代碼:PrintWriter vs PrintStream vs OutputStreamWriter timecosts

import java.io.FileOutputStream; 
import java.io.OutputStreamWriter; 
import java.io.PrintStream; 
import java.io.PrintWriter; 

public class IOtests 
{ 

public static void main(String[] args) throws Exception 
{ 
    char[] chars = new char[100]; 
    byte[] bytes = new byte[100]; 
    for (int i = 0; i < 100; i++) 
    { 
     chars[i] = (char) i; 
     bytes[i] = (byte) i; 
    } 
    OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(
      "output.txt")); 
    long a = System.currentTimeMillis(); 
    for (int i = 0; i < 100000; i++) 
     for (char j : chars) 
      out.write(j); 
    System.out.println("OutputStreamWriter writing characters: " 
      + (System.currentTimeMillis() - a)); 
    out = new OutputStreamWriter(new FileOutputStream("output.txt")); 
    a = System.currentTimeMillis(); 
    for (int i = 0; i < 100000; i++) 
     for (byte j : bytes) 
      out.write(j); 
    System.out.println("OutputStreamWriter writing bytes: " 
      + (System.currentTimeMillis() - a)); 
    PrintStream out1 = new PrintStream("output.txt"); 
    a = System.currentTimeMillis(); 
    for (int i = 0; i < 100000; i++) 
     for (char j : chars) 
      out1.write(j); 
    System.out.println("PrintStream writing characters: " 
      + (System.currentTimeMillis() - a)); 
    out1 = new PrintStream("output.txt"); 
    a = System.currentTimeMillis(); 
    for (int i = 0; i < 100000; i++) 
     for (byte j : bytes) 
      out1.write(j); 
    System.out.println("PrintStream writing bytes: " 
      + (System.currentTimeMillis() - a)); 
    PrintWriter out2 = new PrintWriter("output.txt"); 
    a = System.currentTimeMillis(); 
    for (int i = 0; i < 100000; i++) 
     for (char j : chars) 
      out2.write(j); 
    System.out.println("PrintWriter writing characters: " 
      + (System.currentTimeMillis() - a)); 
    out1 = new PrintStream("output.txt"); 
    a = System.currentTimeMillis(); 
    for (int i = 0; i < 100000; i++) 
     for (byte j : bytes) 
      out2.write(j); 
    System.out.println("PrintWriter writing bytes: " 
      + (System.currentTimeMillis() - a)); 
} 

} 

結果:

OutputStreamWriter書寫字符:4141個
OutputStreamWriter寫入字節:3546個
的PrintStream書寫字符:86516個
的PrintStream寫入字節:70484
PrintWriter寫人物:938
PrintWriter寫作字節:2484

請注意,所有時間都是以毫秒爲單位。

+1

鑑於您從不*關閉輸出,它可以全部被緩衝。此外,你沒有給任何JIT熱身,沒有執行任何垃圾收集等。哦,和「PrintWriter寫字節」是錯誤的,因爲它只寫*字符*。你剛剛得到一個隱式字節到整數轉換。此外,在大多數合理的代碼中,一次寫入單個字節或字符是不現實的 - 您可以使用採用'byte []','char []'或'String'的重載。 –

+1

您的時間安排非常可疑。有關如何在Java中編寫正確的基準測試的信息,請參見[本主題](http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java)。 –

+1

@TedHopp隨着所有建議的採取,寫入磁盤上的文件本身就是一個不可預知的過程。如果在* nix上,可以使用'/ dev/null'作爲相對可預測的接收器;在Windows上,天知道什麼。 –

回答

3

我已經減少了你的問題的本質:

public class Test { 
    static byte[] bytes = new byte[10_000_000]; 
    static { 
    for (int i = 0; i < bytes.length; i++) bytes[i] = (byte) (i%100+32); 
    } 
    public static void main(String[] args) throws Exception { 
    writer(true); 
    writer(false); 
    stream(true); 
    stream(false); 
    } 

    static void writer(boolean flush) throws IOException { 
    Writer out = new FileWriter("output.txt"); 
    long a = System.currentTimeMillis(); 
    for (byte j : bytes) { 
     out.write(j); 
     if (flush) out.flush(); 
    } 
    out.close(); 
    System.out.println("FileWriter with" + (flush? "":"out") + " flushing: " + 
     (System.currentTimeMillis() - a)); 
    } 
    static void stream(boolean flush) throws IOException { 
    OutputStream out = new FileOutputStream("output.txt"); 
    long a = System.currentTimeMillis(); 
    for (byte j : bytes) { 
     out.write(j); 
     if (flush) out.flush(); 
    } 
    out.close(); 
    System.out.println("FileOutputStream with" + (flush? "":"out") + " flushing: " + 
     (System.currentTimeMillis() - a)); 
    } 
} 

注:

  • 完成時正確關閉的資源;
  • 雙循環被單循環取代,而是一個更大的數組;
  • 避免編寫控制字符來逃避自動刷新行爲;
  • 只使用字節數組,因爲您在所有情況下只測試一種方法:write(int)。因此,使用字節還是字符無關緊要;
  • 刪除了除FileWriterFileOutputStream之外的所有內容,因爲所有其他情況歸結爲這兩個;
  • 以兩種模式測試寫入器和輸出流:在每次寫入之後刷新,直到關閉時才刷新。

現在,當你運行它,你會得到類似以下的輸出:

FileWriter with flushing: 28235 
FileWriter without flushing: 828 
FileOutputStream with flushing: 23984 
FileOutputStream without flushing: 23641 

那麼,有什麼教訓?

  • 所有的寫入程序都被緩衝,因爲它們在內部被委託給本身被緩衝的StreamEncoder;
  • FileOutputStream未被緩衝;
  • 非緩衝寫入字節一個字節是非常慢的。

良好做法的需求,你總是寫入緩存:要麼使用緩衝片,或維持在您身邊一個明確的緩衝。

+0

真的,我無法理解PrintWriter,PrintStream和OutputStreamWriter之間的差異,並且使用文件僅僅是一個例子,因爲我只需要編寫如此多的字符/字節來比較操作速率和文件作爲流的接收器非常合適。因此,如果不考慮寫入文件而不使用緩衝區的這一事實很慢,則假定我正在寫入終端。現在我必須使用哪個工具? –

+0

無關緊要:無緩衝的I/O速度很慢,因爲它涉及每個字節一個系統調用。所以這些問題不僅適用於寫入某個「虛擬」流時,它不涉及JVM本身之外的任何操作。例如,'StringWriter'或'ByteArrayOutputStream'。 –

+0

至於'PrintWriter'與'PrintStream',後者是一個遺留類。編寫字符數據時,您應該總是喜歡使用'Writer',寫入二進制數據時應該使用'OutputStream'。 –

相關問題