IO與NIO常用JAVA寫API的用法和速度對比

最近把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));
	}

以上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章