在测试虚拟硬盘的时候,查了一下基本的读取文件的方法,现在将各个方法列举如下,以及测试速度的时间,供大家作为参考,以后如果用到了,可选用合适的读取方式。
1. BufferedReader,BufferedWriter
这一种方法是实验室常用的一种方式,这种方式的好处是可以读取一行,然后针对每一行进行处理,他是构造一个缓冲流,然后进行读写,但是这种方法是相对比较慢的。
参考代码如下:- BufferedReader br = new BufferedReader(new FileReader(args[0]));
- BufferedWriter bw=new BufferedWriter(new FileWriter(args[1]));
- String line;
- while((line=br.readLine())!=null)
- {
- bw.write(line);
- }
- bw.flush();
- bw.close();
- br.close();
复制代码 参数可以自己选用。
2. RandomAccessFile
这一种方法不常用,是随机读取的方式,这种方式比较特殊,他不隶属于InputStream,OutputStream类系,他是直接继承自Object类的。RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream粘起来,再加上它自己的一些方法,比如定位用的getFilePointer( ),在文件里移动用的seek( ),以及判断文件大小的length( )。此外,它的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数 (和C的fopen( )一模一样)。它不支持只写文件,从这一点上看,假如RandomAccessFile继承了DataInputStream,它也许会干得更好。
参考代码如下:- RandomAccessFile read = new RandomAccessFile(args[0],"r");
- RandomAccessFile writer = new RandomAccessFile(args[1],"rw");
- byte[] b = new byte[1024*1024];
- int count;
- while((count=read.read(b))!=-1){
- //System.out.println(count);
- if(count!=b.length)
- {
- byte[] t=new byte[count];
- for(int i=0;i<count;++i)
- t[i]=b[i];
- writer.write(t);
- }
- else writer.write(b);
- }
- writer.close();
- read.close();
复制代码 参数选择可以根据后边标记读写的进行选择,比如说第二个参数是"r",说明这是一个读文件的,如果是"rw",说明是写文件的。
3. FileInputStream,FileOutputSteam
这是一个最基本的读写文件的方式,这种读写一般是读取一个字节,或者定义长度的字节个数,如果为了节约时间的话,建议用缓冲区读写,这样速度会快一点。
参考代码如下:- InputStream in=new FileInputStream(new File(args[0]));
- OutputStream out=new FileOutputStream(new File(args[1]));
- int count;
- byte[] b=new byte[1024*1024];
- while((count=in.read(b))!=-1)
- {
- if(count!=b.length)
- {
- byte[] t=new byte[count];
- for(int i=0;i<count;++i)
- t[i]=b[i];
- out.write(t);
- }
- else out.write(b);
- }
- in.close();
- out.flush();
- out.close();
复制代码 参数和第一个选择一样。
4. 内存直接映射
这是一个内存直接映射读取的方式,如果文件大小合适,其他读取方式相对于他来说就是浮云啊,等一下我把各个大小的比对时间放上,大家应该可以看出来。但是这有一个限制,就是如果文件太大的话,映射内存会出现问题,要么就是内存不够,或者就是读取很慢,我觉得应该是文件太大,映射内存会出现不够的现象,然后内存会启动换入换出之类的,会浪费时间。但是基本的文件读写,他还是秒杀的。
参考代码如下:- FileChannel read = new RandomAccessFile(args[0],"r").getChannel();
- FileChannel writer = new RandomAccessFile(args[1],"rw").getChannel();
- long i = 0;
- long size = read.size()/30;
- //System.out.println(read.size());
- ByteBuffer bb,cc = null;
- while(i<read.size()&&(read.size()-i)>size)
- {
- bb = read.map(FileChannel.MapMode.READ_ONLY, i, size);
- cc = writer.map(FileChannel.MapMode.READ_WRITE, i, size);
- cc.put(bb);
- i+=size;
- bb.clear();
- cc.clear();
- }
- bb = read.map(FileChannel.MapMode.READ_ONLY, i, read.size()-i);
- cc = writer.map(FileChannel.MapMode.READ_WRITE, i, read.size()-i);
- cc.put(bb);
- bb.clear();
- cc.clear();
- read.close();
- writer.close();
复制代码 参数同上。
下边我列出以下各个大小文件,各种读取方式所花费的时间,仅作参考。
表格里边我会用Buffer作为BufferedReader的简写,Random作为RandomAccessFile的简写,FileInput作为FileInputStream的简写,Map作为内存直接映射的简写。我给出两次测试结果。
146M文件 | 第一次测试 | 第二次测试 | Buffer | 4.737s | 4.581s | Random | 0.251s | 0.249s | FileInput | 0.27s | 0.269s | Map | 0.102s | 0.104s |
除了BufferedReader之外,其他的读取时间基本上没有太大区别。
356M文件 | 第一次测试 | 第二次测试 | Buffer | 10.889s | 10.71s | Random | 0.558s | 0.589s | FileInput | 0.637s | 0.635s | Map | 0.125s | 0.124s |
570M文件 | 第一次测试 | 第二次测试 | Buffer | 17.215s | 17.393s | Random | 2.756s | 2.243s | FileInput | 1.924s | 1.975s | Map | 0.203s | 0.197s |
712M文件 | 第一次测试 | 第二次测试 | Buffer | 21.852s | 23.262s | Random | 3.481s | 3.529s | FileInput | 3.829s | 3.42s | Map | 0.246s | 0.258s |
998M文件 | 第一次测试 | 第二次测试 | Buffer | 34.08s | 32.437s | Random | 21.817s | 20.589s | FileInput | 9.669s | 9.792s | Map | 15.481s | 16.886s |
由以上图表可以看出,在文件大小一般的情况下,随着文件大小的增加,内存映射基本保持在1秒以下,基本0.2s左右,而其他的都会随着文件的增大有明显的变化,这就是内存直接映射秒杀的地方,但是就是文件增加到了998M之后,时间突然之间会增加很大,不知道是不是因为文件太大,以致于内存里边需要执行操作系统提供的一些保护算法而浪费时间了,这个有待考证,也可能是每次运行完之后没有清理内存,导致数据堵塞,因为内存映射方式,java没有提供相应的直接回收MappedByteBuffer区域的方法,这也会导致另外一种异常就是内存不够。
然后我在网上搜了两种解决方式,但是我自己也没有实验出来,代码是执行了,但是不知道从哪里看出他执行前后的区别。这里也把代码放上。
第一种方式:- System.gc();
- System.runFinalization();
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
-
- e.printStackTrace();
- }
复制代码 第二种方式:- public static void unmap(final MappedByteBuffer buffer) {
- if (buffer == null) {
- return;
- }
- AccessController.doPrivileged(new PrivilegedAction<Object>() {
- public Object run() {
- try {
- Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
- if (getCleanerMethod != null) {
- getCleanerMethod.setAccessible(true);
- Object cleaner = getCleanerMethod.invoke(buffer, new Object[0]);
- Method cleanMethod = cleaner.getClass().getMethod("clean", new Class[0]);
- if (cleanMethod != null) {
- cleanMethod.invoke(cleaner, new Object[0]);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
- });
- }
复制代码 但是执行这个之后,会把时间拖慢,也是一个问题,所以这个问题待解决。
上边是我测试了几个方式的读取方式,这样以后实验室的使用读取文件的方式的时候,可以选用最适合的方式,如果时间是最紧要的,那么可以选用内存直接映射,但是这个要保证内存够用,而且文件合适的情况下,至于上边的那个问题,下边我会继续关注,如果谁了解了,欢迎提示~
如果谁熟悉其他的方式的,欢迎跟帖~
|