本文展示BIO,NIO复制文件的不同写法,和一丢丢的效率对比。
在演示NIO之前 ,先简单说一下 NIO
在BIO中,根据方向的不同,分为输入流,输出流,而在NIO中,就没有这个概念了,NIO中,提出了Channel的概念,一个通道是可以读也可以写的。channel通过和Buffer的交互,可以往buffer写数据也可以读数据,这是怎么实现的呢?
首先,Buffer是一个数组,有三个重要的指针。
buffer的大小/容量 - Capacity
当前读/写的位置 - Position
信息末尾的位置 - limit
初始化的时候,他们的分布是这个样子的(找文件夹的图片真的太难翻了,我直接截图我的MD)。
假设写了3份数据。
这个时候想读取数据了,这个时候调用flip()函数,会发生这样的过程。
position回到起点,limit指针在原来position的位置。
假设数据读取完毕,调用clear()函数,buffer又会变成原来的样子,变成写入模式。
如果在读数据的时候,并没有读取完数据,还想下次继续读取且不影响写入呢
此时调用compact()函数,buffer会拷贝没有读取完的数据从起点开始存放,并且调整指针。
这样大概明白了吧,但是Channel之间也是可以之间通信的。
下面展示复制文件的用法
为了使用上的方便,首先定义一个文件复制的接口。
public interface FileCopyRunner {
void copyFile(File source,File target);
}
main函数中分别实现不同的方式:
FileCopyRunner noBufferStreamCopy;
FileCopyRunner bufferStreamCopy;
FileCopyRunner nioBufferCopy;
//two channel
FileCopyRunner nioTransferCopy;
noBufferStreamCopy:
noBufferStreamCopy = new FileCopyRunner() {
@Override
public void copyFile(File source, File target) {
InputStream fin = null;
OutputStream fout = null;
try {
fin = new FileInputStream(source);
fout = new FileOutputStream(target);
int result;
while ((result = fin.read()) != -1) {
fout.write(result);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
close(fin);
close(fout);
}
}
@Override
public String toString() {
return "noBufferStreamCopy";
}
};
bufferStreamCopy:
bufferStreamCopy = new FileCopyRunner() {
@Override
public void copyFile(File source, File target) {
InputStream fin = null;
OutputStream fout = null;
try {
fin = new BufferedInputStream(new FileInputStream(source));
fout = new BufferedOutputStream(new FileOutputStream(target));
byte[] buffer = new byte[1024];
int result;
while ((result = fin.read(buffer)) != -1){
fout.write(buffer,0,result);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
close(fin);
close(fout);
}
}
@Override
public String toString() {
return "bufferStreamCopy";
}
};
nioBufferCopy:
nioBufferCopy = new FileCopyRunner() {
@Override
public String toString() {
return "nioBufferCopy";
}
@Override
public void copyFile(File source, File target) {
FileChannel fin = null;
FileChannel fout = null;
try {
fin = new FileInputStream(source).getChannel();
fout = new FileOutputStream(target).getChannel();
ByteBuffer buffer =ByteBuffer.allocate(1024);
while ((fin.read(buffer)) != -1){
//转换模式
buffer.flip();
while (buffer.hasRemaining()){
fout.write(buffer);
}
//写完后,再把指针还原
buffer .clear();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
close(fin);
close(fout);
}
}
};
nioTransferCopy:
nioTransferCopy = new FileCopyRunner() {
@Override
public String toString() {
return "nioBufferCopy";
}
@Override
public void copyFile(File source, File target) {
FileChannel fin = null;
FileChannel fout = null;
try {
fin = new FileInputStream(source).getChannel();
fout = new FileOutputStream(target).getChannel();
long transfer =0L;
long size = fin.size();
//开始拷贝数据的位置,
while (transfer != fin.size()) {
transfer += fin.transferTo(0, size, fout);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
close(fin);
close(fout);
}
}
};
平均复制粘贴5次比较效率 :
private static final int ROUNDS = 5;
private static void benchmark(FileCopyRunner test,File source,File target){
long elapsed = 0L;
for (int i = 0; i <ROUNDS ; i++) {
long startTime = System.currentTimeMillis();
test.copyFile(source,target);
long endTime = System.currentTimeMillis();
elapsed+=endTime-startTime;
//最后要删的哈
target.delete();
}
System.out.println(test + ":" + elapsed/ROUNDS );
}
File sourceFile = new File("C:/Users/12479/Desktop/SB核心篇章/源码、资料、课件.rar");
File targetFile = new File("C:/Users/12479/Desktop/SB核心篇章/n源码、资料、课件.rar");
// benchmark(noBufferStreamCopy,sourceFile,targetFile);
benchmark(bufferStreamCopy,sourceFile,targetFile);
benchmark(nioBufferCopy,sourceFile,targetFile);
benchmark(nioTransferCopy,sourceFile,targetFile);
知道我为啥把第一个注释了吗?最开始我是没有注释的,但是惊人的事情发生了,我开始测试的文件是1.5Gb,5个来回7个多G吧?我的天呐,我12点吃饭我吃完饭等到1点啊!!
我就直接贴结果了:
/**
- 80M
- noBufferStreamCopy:326448
- bufferStreamCopy:159
- nioBufferCopy:486
- nioBufferCopy:81
- 1.5GB
- bufferStreamCopy:27624
- nioBufferCopy:35141
- nioBufferCopy:21027
*/
下篇预告:基于NIO模型实现聊天室