生動講解使用不同方式操作File檔案的方法之間的差異

不設限發表於2011-12-09
下面展示了兩種java檔案操作的方式,

第一種是通過流來實現對檔案的複製
第二種是通過reader的方式來實現的
第三種是通過buffer來實現的
而且每一個方法之後都附有了對應的方法的使用btye[]/char[]的方法,讀者可以自行的測試它們的執行時間來比較它們的效能的優劣

package com.dada.handlefile;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Date;

public class CopyFile {
	public static void main(String[] args) throws Exception {
		CopyFile cf = new CopyFile();
		File source = new File("source.JPG");
		File dest = new File("dest.JPG");
		File dest1 = new File("dest1.JPG");
		
//		cf.check(source);
//		// 測試第一種方法執行的時間
//		File dest1 = new File("temp1.jpg");
//		cf.check(dest1);
//		Date m1Begin = new Date();
//		cf.createFileByStream(source, dest1);
//		Date m1End = new Date();
//		long t1 = (m1End.getTime() - m1Begin.getTime());
//
//		// 測試第二種方法執行的時間
//		File dest2 = new File("temp2.jpg");
//		cf.check(dest2);
//		Date m2Begin = new Date();
//		cf.createFileByReader(source, dest2);
//		Date m2End = new Date();
//		long t2 = (m2End.getTime() - m2Begin.getTime());
//
//		// 第三種方法執行的時間
//		File dest3 = new File("temp3.jpg");
//		cf.check(dest3);
//		Date m3Begin = new Date();
//		cf.createFileByBuffer(source, dest3);
//		Date m3End = new Date();
//		long t3 = (m3End.getTime() - m3Begin.getTime());
//
//		// 第一和第二種方法之間時間只差
//		long difM1ToM2 = t1 - t2;
//		// 倍數
//		double multipleM1ToM2 = t1 / t2 * 1.0;
//
//		long difM2ToM3 = t2 - t3;
//		double multipleM2ToM3 = t2 / t3 * 1.0;
//
//		long difM1ToM3 = t1 - t3;
//		double multipleM1ToM3 = t1 / t3 * 1.0;
//
//		System.out.println("第一個方法執行時間: " + t1 + "\t第一和第二個方法執行的時間差: "
//				+ difM1ToM2 + "\t倍數差: " + multipleM1ToM2);
//		System.out.println("第二個方法執行時間: " + t2 + "\t第二和第三個方法執行的時間差: "
//				+ difM2ToM3 + "\t倍數差: " + multipleM2ToM3);
//		System.out.println("第三個方法執行時間: " + t3 + "\t第一和第三個方法執行的時間差: "
//				+ difM1ToM3 + "\t倍數差: " + multipleM1ToM3);
		
	}

	/**
	 * 檢測指定檔案是否存在,不存在就建立
	 * 
	 * @param f
	 */
	public void check(File f) {
		if (!f.exists()) {
			try {
				f.createNewFile();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * 通過stream來複制檔案
	 * @param source
	 * @param dest
	 * @throws Exception
	 */
	public void createFileByStream(File source, File dest) throws Exception {
		FileInputStream in = new FileInputStream(source);
		FileOutputStream out = new FileOutputStream(dest);
		int read = in.read();
		while (read != -1) {
			out.write(read);
			read = in.read();
		}
		out.close();
		in.close();
	}

	public void createFileByStreamWithByte(File source, File dest) throws Exception {
		FileInputStream in = new FileInputStream(source);
		FileOutputStream out = new FileOutputStream(dest);
		byte[] buf = new byte[1024];
		while (in.read(buf) != -1) {
			out.write(buf);
		}
		out.close();
		in.close();
	}

	/**
	 * 使用reader來複制檔案
	 * @param source
	 * @param dest
	 * @throws Exception
	 */
	public void createFileByReader(File source, File dest) throws Exception {
		FileReader fr = new FileReader(source);
		FileWriter fw = new FileWriter(dest);
		int read = fr.read();
		while (read != -1) {
			fw.write(read);
			read = fr.read();
		}
		fw.close();
		fr.close();
	}
	
	public void createFileByReaderWithChar(File source, File dest) throws Exception {
		FileReader fr = new FileReader(source);
		FileWriter fw = new FileWriter(dest);
		char[] buf = new char[2048];
		while (fr.read(buf) != -1) {
			fw.write(buf);
		}
		fw.close();
		fr.close();
	}

	/**
	 * 通過buffer來複制檔案
	 * @param source
	 * @param dest
	 * @throws Exception
	 */
	public void createFileByBuffer(File source, File dest) throws Exception {
		// 封裝stream
		FileInputStream in = new FileInputStream(source);
		// 把stream封裝成為reader
		InputStreamReader isr = new InputStreamReader(in);
		// 把reader封裝成為buffer
		BufferedReader br = new BufferedReader(isr);
		FileOutputStream out = new FileOutputStream(dest);
		OutputStreamWriter osw = new OutputStreamWriter(out);
		BufferedWriter bw = new BufferedWriter(osw);
		int read = br.read();
		while (read != -1) {
			bw.write(read);
			read = br.read();
		}
		bw.close();
		osw.close();
		out.close();
		br.close();
		isr.close();
		in.close();
	}
	
	public void createFileByBufferWithChar(File source, File dest) throws Exception {
		// 封裝stream
		FileInputStream in = new FileInputStream(source);
		// 把stream封裝成為reader
		InputStreamReader isr = new InputStreamReader(in);
		// 把reader封裝成為buffer
		BufferedReader br = new BufferedReader(isr);
		FileOutputStream out = new FileOutputStream(dest);
		OutputStreamWriter osw = new OutputStreamWriter(out);
		BufferedWriter bw = new BufferedWriter(osw);
		char[] buf = new char[2048];
		while (br.read(buf) != -1) {
			bw.write(buf);
		}
		bw.close();
		osw.close();
		out.close();
		br.close();
		isr.close();
		in.close();
	}
}
執行結果:

 

第一個方法執行時間: 7019  第一和第二個方法執行的時間差: 6894   倍數差: 56.0
第二個方法執行時間: 125    第二和第三個方法執行的時間差: 79        倍數差: 2.0
第三個方法執行時間: 46      第一和第三個方法執行的時間差: 6973    倍數差: 152.0

 

可見差異是非常之大的,相差超過150倍這個,

為什麼呢?我覺得就好像是這樣的:

我們可以打個比喻:

我們把檔案的複製可以看成是在兩個地方之間搬運東西,

有三種運輸工具:

摩托車(對應inputStream/outputStream),

三馬車(對應InputStreamReader/outtStreamReader),

大卡車(對應BufferedReader/BufferedWriter),

它們之間的關係式摩托車可以單獨的運送,送到指定地點之後

回來再次重複原來的動作,也可以把它上面的傳送帶(inputStream

可以封裝進入inputStreamReader裡面),同理三馬車也可以單獨

的運送,也可以把它的傳送帶(inputStreamReader可以封裝進入

BufferedReader裡面)連線到卡車上,但是因為卡車太高了,所以土

無法直接的裝進卡車,所以如果要使用卡車運送土的話,就需要摩托

車和三馬車提供傳送帶,把土送進卡車裡(對應stream的兩個封裝

過程)。

這是讀取過程,寫入的過程也是一樣的。需要把土從卡車上面通過

傳送帶一級一級的傳輸到地面。

 

那麼直接使用摩托車,三馬車和三個一起使用的效率差在哪裡了?

其實這中間取資料和讀資料的速度應該是沒有太大差異的,差異就在

於它們取的頻率差異很大,第一種方法是stream是位元組它們的容量很

小,所以它們每一次只能夠傳輸幾個位元組,而以此類推越往上一次傳輸

的容量越大,這樣的話,往返的次數就少了,花費在"路"上面的時間就少

了,同樣是運送100立方米的土,卡車要一次一個小時就夠了,而三馬車

要10次每一次要3個小時,而摩托車要100次每一次6個小時,這時間差

異就很明顯了.

 

另外在讀寫檔案的時候最好使用各個物件的read()方法,因為這個方法

是以位元組的方式讀取的,不論是什麼格式的檔案都不會出錯,但是如果

你使用buffer的readLine()方法,或者是它的read(byte[] b);方法,結果都

可能會出現問題,比如用readLine()方法讀取文字檔案,在你再次的寫出

去的時候,它裡面的所有的換行都會被去掉的.

如果是用它來讀取其他格式檔案,比如jpg檔案結果基本上都是錯誤的,

所以說在複製的時候直接就呼叫它的read()方法,既簡單效率又高,還不

會出錯的。


 

 

 

相關文章