大家可能都知道带Buffered的IO流应该比普通的IO流更有效率,因为带Buffer的IO流就是为效率而创造出来的,而实际可能并不是在任何情况下都会这样。
在使用Buffer上,实际FileInputStream和BufferedFileInputStream并没有区别,为什么呢,先看如下一段使用BufferedFileInputStream的代码:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
byte[] buffer = new byte[bufferSize];
int copySize = 0;
while((copySize=bis.read(buffer))>0){
bis.write(buffer,0,bufferSize);
//bis.flush();
}
一般大家看到这样的代码,会认为自己的BufferedInputStream使用了缓冲区,确实,它使用了缓冲区,但这个缓冲区是我们自己定义的即这段程序:
byte[] buffer = new byte[bufferSize];
并非是BufferedInputStream自己内部的缓冲区buf[],这个buf[]在源码中如下:
public
class BufferedInputStream extends FilterInputStream {
private static int DEFAULT_BUFFER_SIZE = 8192;
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
/**
* The internal buffer array where the data is stored. When necessary,
* it may be replaced by another array of
* a different size.
*/
protected volatile byte buf[];
之所以这么说,是因为在使用read(byte[] buffer)上,BufferedInputStream和FileInputStream没有任何区别,因为read(byte[] buffer)方法就是BufferedInputStream继承自FileInputStream的。
所以Buffer类的IO流在效率上比普通IO流要高主要体现在输出流上,输入流上没有差别,原因如下:
1. FileOutputStream的Write方法是直接的IO流,没有用到缓冲区:
public void write(byte b[], int off, int len) throws IOException {
writeBytes(b, off, len, append);
}
private native void writeBytes(byte b[], int off, int len, boolean append)
throws IOException;
可以从源码中看到,write方法调用的是wireBytes方法,这是一个native的方法,直接去操作系统了。
2. BufferOutputStream的Write方法是用到了缓冲区的,在某些情况下效率会更高(某些情况下效率会更低,本文后面说明):
public synchronized void write(byte b[], int off, int len) throws IOException {
if (len >= buf.length) {
/* If the request length exceeds the size of the output buffer,
flush the output buffer and then write the data directly.
In this way buffered streams will cascade harmlessly. */
flushBuffer();
out.write(b, off, len);
return;
}
if (len > buf.length - count) {
flushBuffer();
}
System.arraycopy(b, off, buf, count, len);
count += len;
}
从源码中可以看到,BufferedOutputStream在wirte时,会先判断要写的数据是否大于自己的缓冲区,大于缓冲区则直接flush缓冲区和数据,小于缓冲区则往缓冲区装,直到装不下了再flush缓冲区。当然,最后的Write还是调用FileOutputStream的write方法来实现,只不过用了缓冲区后减少了IO次数。
而这样做就是否在任何情况下都能提高效率呢?不是的!
因为BufferedOutputStream的缓冲区大小上面是有讲究的。默认的大小是8192即8k,如果在BufferedInputStream中一次读入的数据远远小于8K(即上文中我们提到的自定义缓冲区bufferSize的大小),BufferedOutputStream比FileOutputStream是有优势的,因为BufferedOutputStream明显IO次数要比FileOutputStream小,越小则BufferedOutputStream的优势越明显。
经实验证实:
1. bufferSize在4096及以下,BufferedOutputStream是有优势的
2. bufferSize在4097-8191之间,FilOutputSteam反而效率更高,因为在这一段2者的IO次数差不多,而BufferedOutputStream要操作2次缓冲区后才能flush
3. bufferSize大于等于8192时,BufferedOutputStream和FilOutputSteam效率就基本一样了