IO与NIO常用JAVA写API的用法和速度对比

2 篇文章 0 订阅

最近把IO和NIO相关文档复习了下,抽空作下总结。

1,常用API的用法,这里只考虑写操作,读操作下次专门写。

2,API之间的效率,及系统相关影响。

3,模拟最常用的写字符文件。

4,对比两类大小的文件:100M和1G。

5,尽量排除字符转字节带来的性能影响。

6,本人电脑配置I7-4790U,硬盘是SSD。

主要对比API效率如下:

1,FileOutputStream:没缓存速度慢,基于字节处理。

2,BufferedOutputStream:速度快

3,FileWriter:没缓存速度最慢,基于字符处理。

4,BufferedWriter:速度快。和BufferedOutputStream差不多,主要慢在底层字符转字节需要处理。

5,PrintWriter:速度快,封装了BufferedWriter,有处理文件行方法。

6,RandomAccessFile:速度慢,可以操作文件指针。

7,ByteBuffer:速度很快,但是需要注意使用方法。加大每次写入块的大小,可以明显提升速度。但是如果一行一行的写入文件,效率还是很差。

8,MappedByteBuffer:速度最快,秒杀上面各位。功能上是RandomAccessFile的爸爸。但是缺陷也有,消耗内存很大。另外处理最大文件大小为2G,主要是size参数为int。

其实上面有几个是差不多的类,无非只是用了装饰器模式。

可能是我电脑的原因,写入100M文件时大家效率差距还挺大的,但是写入1G时,差距会缩小。

内存消耗除了MappedByteBuffer,其实都差不多。如果带有flush()操作,消耗会少点。

 

下面直接贴测试代码同时也是API的用法。

1,FileOutputStream:

    写一个大约100m文件
    时间消耗:1726ms
    写一个大约1G文件
    时间消耗:18074ms,内存消耗约:350M+

 


	public static void writeFileStream() throws Exception {
		long start = System.currentTimeMillis();
		System.out.println("writeBuffer-开始时间:"+start);
		FileOutputStream fos =new FileOutputStream("file.txt");
		String str = "我们的家在东北的松花江上,我们的家在东北的松花江上,我们的家在东北的松花江上\n";
		byte[] bytes = str.getBytes();
		for(int i = 0 ; i<10000000;i++) {
			fos.write(bytes);
			fos.flush();
		}
		fos.close();
		long end = System.currentTimeMillis();
		System.out.println("总消耗:"+(end-start));
	}

2,BufferedOutputStream

    写一个大约100m文件
    时间消耗:157ms
    写一个大约1G文件
    时间消耗:8740ms,内存消耗300M+

	public static void bufferFileStream() throws Exception {
		long start = System.currentTimeMillis();
		System.out.println("writeBuffer-开始时间:"+start);
		FileOutputStream fos =new FileOutputStream("file1.txt");
		//默认缓存区大小为8192,扩大缓存区提升效果不明显(缓存区扩大5倍时100M的写入速度大约为120ms)
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		String str = "我们的家在东北的松花江上,我们的家在东北的松花江上,我们的家在东北的松花江上\n";
		byte[] bytes = str.getBytes();
		for(int i = 0 ; i<10000000;i++) {
			bos.write(bytes);
		}
		bos.close();
		long end = System.currentTimeMillis();
		System.out.println("总消耗:"+(end-start));
	}

3,FileWriter

    写一个大约100m文件
    时间消耗:2000ms。
    写一个大约1G文件
    时间消耗:23484ms。内存消耗:150M+

	public static void writerFile() throws Exception {
		long start = System.currentTimeMillis();
		System.out.println("writeBuffer-开始时间:"+start);
		FileWriter w = new FileWriter("file2.txt");
		String str = "我们的家在东北的松花江上,我们的家在东北的松花江上,我们的家在东北的松花江上\n";
		for(int i = 0 ; i<1000000;i++) {
			w.write(str);
			w.flush();//这里一般情况是要flush,虽然不加速度快很多。但是容易丢失数据,也容易内存溢出。
		}
		w.close();
		long end = System.currentTimeMillis();
		System.out.println("总消耗:"+(end-start));
	}

4,BufferedWriter

    写一个大约1G文件
    时间消耗:8509ms。
    写一个大约100m文件
    时间消耗:270ms。内存消耗:450M+

	public static void bufferWriterFile() throws Exception {
		long start = System.currentTimeMillis();
		System.out.println("writeBuffer-开始时间:"+start);
		//缓存区扩大后,和BufferedOutputStream类一样,效果不明显。所有还得根据实际情况进行调整。
		//BufferedWriter比BufferedOutputStream慢的原因,主要时字符串还需要转字节处理。其实两者差距很小。
		//只是应用的场景不同而已
		BufferedWriter bw = new BufferedWriter(new FileWriter("file3.txt"));
		String str = "我们的家在东北的松花江上,我们的家在东北的松花江上,我们的家在东北的松花江上\n";
		for(int i = 0 ; i<10000000;i++) {
			bw.write(str);
		}
		bw.close();
		long end = System.currentTimeMillis();
		System.out.println("总消耗:"+(end-start));
	}

5,PrintWriter

     底层其实也是bufferWriter但是封装了一些处理文件行的方法

    写一个大约1G文件
    时间消耗:8553ms
    写一个大约100m文件
    时间消耗:320ms

	public static void printWriter() throws Exception {
		long start = System.currentTimeMillis();
		System.out.println("writeBuffer-开始时间:"+start);
		PrintWriter pw = new PrintWriter("file4.txt");
		String str = "我们的家在东北的松花江上,我们的家在东北的松花江上,我们的家在东北的松花江上\n";
		for(int i = 0 ; i<1000000;i++) {
			pw.println(str);
		}
		pw.close();
		long end = System.currentTimeMillis();
		System.out.println("总消耗:"+(end-start));
	}

6,RandomAccessFile

    写一个大约1G文件
    时间消耗:8553ms.
    写一个大约100m
    时间消耗:1593ms

	public static void randomAccessFile() throws Exception {
		long start = System.currentTimeMillis();
		System.out.println("writeBuffer-开始时间:"+start);
		RandomAccessFile file = new RandomAccessFile("file5.txt","rw");
		String str = "我们的家在东北的松花江上,我们的家在东北的松花江上,我们的家在东北的松花江上";
		for(int i = 0 ; i<1000000;i++) {
			file.writeUTF(str);
		}
		file.close();
		long end = System.currentTimeMillis();
		System.out.println("总消耗:"+(end-start));
	}

7.1,ByteBuffer

利用NIO的ByteBuffer来直接写,这个地方其实可以优化的,因为这么直接写并不是FileChannel的强项。

    写一个大约100M文件
    时间消耗:1799ms.
    写一个大约1G文件
    时间消耗:18687ms.

	public static void byteBuffer1() throws Exception {
		long start = System.currentTimeMillis();
		System.out.println("writeBuffer-开始时间:"+start);
		FileChannel fc = new FileOutputStream("file6.txt").getChannel();
		ByteBuffer bb =ByteBuffer.allocate(1024);
		byte[] b = "我们的家在东北的松花江上,我们的家在东北的松花江上,我们的家在东北的松花江上\n".getBytes("UTF-8");
		for(int i = 0 ; i<1000000;i++) {
			bb = bb.wrap(b);//不会改变ByteBuffer游标位置.如果使用put()方法会改变游标的位置,再次写的时候需要使用bb.flip();
			while(bb.hasRemaining()) {//确保ByteBuffer中的数据完全写进去了。
				fc.write(bb);
			}
			bb.clear();
		}
		fc.close();
		long end = System.currentTimeMillis();
		System.out.println("总消耗:"+(end-start));
	}

7.2,ByteBuffer

    写一个大约100M文件
    时间消耗:94ms.
    写一个大约1G文件
    时间消耗:7286ms.

    看得出效率很明显的提升,而且在小文件上更占优,估计是文件太大瓶颈在系统层面的IO上。NIO是基于块的操作,但是如果块太小也就没什么优势了,所以这里我们人为加大块的范围,提升效率。

	public static void byteBuffer2() throws Exception {
		long start = System.currentTimeMillis();
		System.out.println("writeBuffer-开始时间:"+start);
		FileChannel fc = new FileOutputStream("file6.txt").getChannel();
		ByteBuffer bb =ByteBuffer.allocate(102400*5);
		byte[] b = "我们的家在东北的松花江上,我们的家在东北的松花江上,我们的家在东北的松花江上\n".getBytes("UTF-8");
		for(int i = 0 ; i<10000000;i++) {
			bb.put(b);
			if(i%500==0||i==9999999) {
				bb.flip();
				while(bb.hasRemaining()) {
					fc.write(bb);
				}
				bb.clear();
			}
		}
		fc.close();
		long end = System.currentTimeMillis();
		System.out.println("总消耗:"+(end-start));
	}

8,MappedByteBuffer

    写一个大约100M文件
    大约消耗78ms
    写一个大约1G的文件
    大约用时735ms,内存消耗:1G+

    这里提升太明显了,简直是秒杀上面各位,当然缺点也很明显内存消耗大。但是文件映射单独作写的操作并不是很好,主要是文件的大小一开始就要设置好,并且固定好了,如果你写不满那么就会浪费,而且文件末尾处的”null“还需要处理下。

我认为MappedByteBuffer更适合复制,修改类操作(当然读也很快),这也是基于“将文件当作数组”思想。

	public static void mappedFileChannel() throws Exception {
		long start = System.currentTimeMillis();
		System.out.println("writeBuffer-开始时间:"+start);
		FileChannel fc = new RandomAccessFile("file7.txt","rw").getChannel();
		byte[] b = "我们的家在东北的松花江上,我们的家在东北的松花江上,我们的家在东北的松花江上\n".getBytes("UTF-8");
		int length= b.length;
		System.out.println("字段大小:"+b.length);
		long size = length*10000000;
		MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, size);
		for(int i=0;i<10000000;i++) {
			mbb.put(b);
		}
		fc.close();
		long end = System.currentTimeMillis();
		System.out.println("总消耗:"+(end-start));
	}

以上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值