近來,在做服務器后台處理數據的時候,需要用到Java自帶的幾種流對數據進行讀寫,初始時沒怎么在意,就隨便用了一個,結果發現性能上並不盡如人意。於是對幾種常用的流做了個小小的性能測試。測試代碼如下:
1 public static int FileOutputStreamTime = 0; 2 public static int BufferedOutputStreamTime = 0; 3 public static int FileWriterTime = 0; 4 public static int FileInputStreamTime = 0; 5 public static int BufferedInputStreamTime = 0; 6 public static int FileReaderTime = 0; 7 8 public static void write(String filePath, String content){ 9 FileOutputStream out = null; 10 FileOutputStream outStr = null; 11 BufferedOutputStream buf = null; 12 FileWriter fw = null; 13 File f = new File(filePath); 14 15 try { 16 //Test FileOutputStream 17 long begin1 = System.currentTimeMillis(); 18 out = new FileOutputStream(f); 19 out.write(content.getBytes()); 20 out.close(); 21 long end1 = System.currentTimeMillis(); 22 FileOutputStreamTime += end1 - begin1; 23 24 //Test BufferedOutputStream 25 long begin2 = System.currentTimeMillis(); 26 outStr = new FileOutputStream(f); 27 buf = new BufferedOutputStream(outStr); 28 buf.write(content.getBytes()); 29 buf.flush(); 30 buf.close(); 31 long end2 = System.currentTimeMillis(); 32 BufferedOutputStreamTime += end2 - begin2; 33 34 //Test FileWriter 35 long begin3 = System.currentTimeMillis(); 36 // the second parameter "true",Whether or not a file will be covered 37 // or appended. 38 fw = new FileWriter(f); 39 // fw = new FileWriter("d:/test/testwrite/add2.txt",true); 40 fw.write(content); 41 fw.close(); 42 long end3 = System.currentTimeMillis(); 43 FileWriterTime += end3 - begin3; 44 } catch (Exception e) { 45 e.printStackTrace(); 46 } finally { 47 try { 48 fw.close(); 49 buf.close(); 50 outStr.close(); 51 out.close(); 52 } catch (Exception e) { 53 e.printStackTrace(); 54 } 55 } 56 } 57 58 public static void read(String filePath){ 59 FileInputStream in = null; 60 BufferedInputStream buf = null; 61 FileReader reader = null; 62 BufferedReader br = null; 63 StringBuffer sb = new StringBuffer(); 64 65 try { 66 //Test FileInputStream 67 long begin1 = System.currentTimeMillis(); 68 File f = new File(filePath); 69 in = new FileInputStream(f); 70 int len1 = 512; 71 byte[] bytes1 = new byte[len1]; 72 73 while ((len1 = in.read(bytes1, 0, len1)) != -1) { 74 if(len1 < 512){ 75 byte[] tmpBuf = new byte[len1]; 76 System.arraycopy(bytes1, 0, tmpBuf, 0, len1); 77 sb.append(new String(tmpBuf)); 78 tmpBuf = null; 79 }else{ 80 sb.append(new String(bytes1)); 81 } 82 } 83 84 in.close(); 85 long end1 = System.currentTimeMillis(); 86 FileInputStreamTime += end1 - begin1; 87 88 //Test BufferedInputStream 89 long begin2 = System.currentTimeMillis(); 90 int len2 = 512; 91 byte[] bytes2 = new byte[len2]; 92 buf = new BufferedInputStream(new FileInputStream(f)); 93 while ((len2 = buf.read(bytes2, 0, len2)) != -1) { 94 if(len2 < 512){ 95 byte[] tmpBuf = new byte[len2]; 96 System.arraycopy(bytes2, 0, tmpBuf, 0, len2); 97 sb.append(new String(tmpBuf)); 98 tmpBuf = null; 99 }else{ 100 sb.append(new String(bytes2)); 101 } 102 } 103 104 buf.close(); 105 long end2 = System.currentTimeMillis(); 106 BufferedInputStreamTime += end2 - begin2; 107 108 //Test FileReader 109 long begin3 = System.currentTimeMillis(); 110 reader = new FileReader(f); 111 br = new BufferedReader(reader); 112 String str; 113 while ((str = br.readLine()) != null) { 114 sb.append(str); 115 } 116 br.close(); 117 reader.close(); 118 long end3 = System.currentTimeMillis(); 119 FileReaderTime += end3 - begin3; 120 121 } catch (Exception e) { 122 e.printStackTrace(); 123 } finally { 124 try { 125 br.close(); 126 reader.close(); 127 in.close(); 128 buf.close(); 129 } catch (Exception e) { 130 e.printStackTrace(); 131 } 132 } 133 }
測試時,分別對不同大小的數據做500次同樣的操作,取得的平均耗時如下:
10K | 100K | 200K | 400K | |
FileOutputStream | 0.496 | 0.794 | 1.078 | 1.602 |
BufferedOutputStream | 0.386 | 0.684 | 0.878 | 1.246 |
FileWriter | 0.586 | 0.996 | 1.3 | 1.946 |
FileInputStream | 0.158 | 0.544 | 0.984 | 1.876 |
BufferedInputStream | 0.152 | 0.396 | 0.668 | 1.068 |
FileReader | 0.152 | 0.608 | 1.254 | 2.19 |
通過測試,可以發現,就寫數據而言,BufferedOutputStream耗時是最短的,而性能FileWriter最差;讀數據方面,BufferedInputStream性能最好,而FileReader性能最差勁。所以,以后在涉及到流的讀寫方面時,要注意一下了~
FileOutputStream 用於寫入諸如圖像數據之類的原始字節的流。要寫入字符流,請考慮使用 FileWriter。
BufferedOutputStream的數據成員buf是一個位數組,默認為512字節。當使用write()方法寫入數據時,實際上會先將數據寫至buf中,當buf已滿時才會實現給定的OutputStream對象的write()方法,將buf數據寫至目的地,而不是每次都對目的地作寫入的動作。BufferedInputStream也是類似。
另外,有時只是要存儲一個對象的成員數據,而不是整個對象的信息,成員數據的類型假設都是Java的基本數據類型,這樣的需求不必要使用到與Object輸入、輸出相關的流對象,可以使用DataInputStream、DataOutputStream來寫入或讀出數據。在從文件中讀出數據時,不用費心地自行判斷讀入字符串時或讀入int類型時何時該停止,使用對應的readUTF()或readInt()方法就可以正確地讀入完整類型數據。
在Java程序執行的過程中,很多數據都是以對象的方式存在於內存中。有時會希望直接將內存中整個對象存儲至文件,而不是只存儲對象中的某些基本類型成員信息,而在下一次程序運行時,希望可以從文件中讀出數據並還原為對象。這時可以使用java.io.ObjectInputStream和java.io.ObjectOutputStream來進行這項工作。
ByteArrayInputStream可以將一個數組當作流輸入的來源,而ByteArrayOutputStream則可以將一個位數組當作流輸出的目的地。在這里舉一個簡單的文件位編輯程序作為例子,您可以打開一個簡單的文本文件,其中有簡單的A、B、C、D、E、F、G等字符,在讀取文件之后,可以直接以程序來指定文件中位的位置來修改所指定的字符。作法是將文件讀入數組中,指定數組索引修改元素,然后重新將數組存回文件。
java.io.Reader和java.io.Writer支持Unicode標准字符集(Character Set)(字節流則只支持ISO-Latin-1 8-bit)。在處理流數據時,會根據系統默認的字符編碼來進行字符轉換,Reader和Writer是抽象類,在進行文本文件的字符讀寫時真正會使用其子類,子類通常會重新定義相關的方法。
如果想要存取的是一個文本文件,可以直接使用java.io.FileReader和java.io.FileWriter類,它們分別繼承自InputStreamReader與OutputStreamWriter。可以直接指定文件名稱或File對象來打開指定的文本文件,並讀入流轉換后的字符,字符的轉換會根據系統默認的編碼(若要指定編碼,則還是使用InputStreamReader與OutputStreamWriter)。
java.io.BufferedReader與java.io.BufferedWriter類各擁有8192字符的緩沖區。當BufferedReader在讀取文本文件時,會先盡量從文件中讀入字符數據並置入緩沖區,而之后若使用read()方法,會先從緩沖區中進行讀取。如果緩沖區數據不足,才會再從文件中讀取,使用BufferedWriter時,寫入的數據並不會先輸出至目的地,而是先存儲至緩沖區中。如果緩沖區中的數據滿了,才會一次對目的地進行寫出。例如一個文件,通過緩沖區可減少對硬盤的輸入/輸出動作,以提高文件存取的效率。