近来,在做服务器后台处理数据的时候,需要用到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次同样的操作,取得的平均耗时如下:
不同流耗时对比(ms)
| 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时,写入的数据并不会先输出至目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。例如一个文件,通过缓冲区可减少对硬盘的输入/输出动作,以提高文件存取的效率。