IO流不同方法copy檔案的效率分析
IO流copy檔案的幾種方法介紹
1、FileInputStream和FileOutputStream:檔案的讀寫一般是以位元組為單位的,前者從檔案中獲取位元組資料,後者將位元組資料寫入檔案。這種方式獲取的物件對於資料的讀寫是逐位元組的,稍後我們將測試其讀寫的速度。在讀寫的過程中我們可以這樣理解,read()方法檢視其底層可以發現是一個native方法,native方法連線了java和底層的C語言,IO流讀取到了資料,儲存在一個8位(一個位元組)的暫存器中,最簡單的檔案流就是對這個暫存器上的資料進行操作的,這也是為什麼這種方法是逐位元組讀寫的原因,這種方式在每次讀完一個位元組的的時候,就會產生溢位中斷,CPU再去響應中斷請求,這樣一來,其效率是很低下的,在讀大檔案的時候,這種方法就捉襟見肘了。
2、BufferedInputStream和BufferedOutputStream:緩衝輸入流和緩衝輸出流,怎麼獲得緩衝流物件呢?我們通過把方法1中的物件用緩衝流的構造方法包裝即可獲得。那麼什麼是緩衝呢?JVM記憶體中為它開闢了一塊區域,用來臨時存放資料,相當於是在方法1中加了一塊區域,讀到的位元組都會經過這個中間環節,先寫到緩衝區,當緩衝區讀滿或者給其相應強制清除緩衝的指令(flush方法)時,就會一次性讀取緩衝區的所有資料。它的優越之處顯而易見,CPU可以有更多的時間去處理別的事務了,一次性地讀取大大提升了讀取的速度,程式在效能上明顯優於方法1,我們在之後的測試中也可以看到。
3、自定義byte[] buf位元組陣列建立緩衝區:和方法2的原理類似,但這種方法使得我們對資料的處理更加有可操作性,我們可以自己定義緩衝區的大小,因為這個空間是我們自己定義的。在流程上,我們先把資料寫入陣列,獲取長度,再把位元組陣列的資料寫到檔案中,這依然是一種批量操作。
測試三種方法copy檔案的速度(java程式)
1、程式碼實現:我這裡分別寫了三個方法,測試程式傳檔案所花費的時間,第三種方法修改入口引數,可以自定義緩衝區位元組陣列的長度。
public class Test { public static void main(String[] args){ Test test = new Test(); test.withoutBuffer();//測試不加緩衝 test.addBuffer(); //測試加緩衝 test.BufferByte(1024);//自定義緩衝陣列長度 } /** * 不加緩衝的檔案傳輸測試 */ public void withoutBuffer(){ File file = new File("E:\\workspace\\mayifan\\src\\com\\myf\\bufferInputStream1211\\send\\雲端計算必讀-Google_三大論文中文版.pdf"); File file1 = new File("E:\\workspace\\mayifan\\src\\com\\myf\\bufferInputStream1211\\get\\雲端計算必讀-Google_三大論文中文版.pdf"); try{ FileInputStream fis = new FileInputStream(file); //獲取檔案輸入流 FileOutputStream fos = new FileOutputStream(file1); //獲取檔案輸出流 int value=0; long startTime = System.currentTimeMillis(); //執行前的時間 while((value = fis.read())!=-1){ //讀取下一個位元組,返回值是位元組對應的位元組對應的位元組值,讀到-1則表示沒有資料了 fos.write(value); //把這個位元組寫出去 } long endTime = System.currentTimeMillis(); //執行後的時間 long time = endTime-startTime; //花費時間 System.out.println("不加緩衝copy檔案用了:"+time+"ms"); fos.close(); //關資源 fis.close(); }catch(IOException e){ e.printStackTrace(); } } /** * 新增緩衝的檔案傳輸測試 */ public void addBuffer(){ File file = new File("E:\\workspace\\mayifan\\src\\com\\myf\\bufferInputStream1211\\send\\雲端計算必讀-Google_三大論文中文版.pdf"); File file1 = new File("E:\\workspace\\mayifan\\src\\com\\myf\\bufferInputStream1211\\get1\\雲端計算必讀-Google_三大論文中文版.pdf"); try{ FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); //把檔案輸入流包裝為緩衝流 FileOutputStream fos = new FileOutputStream(file1); BufferedOutputStream bos = new BufferedOutputStream(fos); //把檔案輸出流包裝為緩衝流 int length=0; long startTime = System.currentTimeMillis(); while((length = bis.read())!=-1){ bos.write(length); } long endTime = System.currentTimeMillis(); long time = endTime-startTime; System.out.println("加緩衝後的檔案傳輸時間:"+time+"ms"); bos.flush(); bos.close(); fos.close(); bis.close(); fis.close(); }catch(IOException e){ e.printStackTrace(); } } /** * 使用自定義的位元組陣列作為緩衝區 */ public void BufferByte(int len){ File file = new File("E:\\workspace\\mayifan\\src\\com\\myf\\bufferInputStream1211\\send\\雲端計算必讀-Google_三大論文中文版.pdf"); File file1 = new File("E:\\workspace\\mayifan\\src\\com\\myf\\bufferInputStream1211\\get2\\雲端計算必讀-Google_三大論文中文版.pdf"); try{ FileInputStream fis = new FileInputStream(file); //獲取檔案輸入流 FileOutputStream fos = new FileOutputStream(file1); //獲取檔案輸出流 int length=0; //記錄每次讀取到的位元組長度 byte[] buf = new byte[len]; //根據輸入值自定義緩衝區長度 long startTime = System.currentTimeMillis(); //執行前的時間 while((length = fis.read(buf))!=-1){ //讀取位元組到位元組陣列中,最多一次讀1024個 fos.write(buf,0,length); //把這些資料寫到檔案 if(length<len) //最後的資料讀完後退出,防止阻塞 break; } long endTime = System.currentTimeMillis(); //執行後的時間 long time = endTime-startTime; //花費時間 System.out.println("自定義緩衝位元組陣列長度為"+len+"位元組時,copy檔案用了:"+time+"ms"); fos.close(); //關資源 fis.close(); }catch(IOException e){ e.printStackTrace(); } } }
2、執行結果:
3、結果分析:觀察資料,我們可以發現,不加緩衝逐位元組讀取花費的時間是很長的,在程式執行的時候我們就可以明顯發現這一點。在加了java自帶的緩衝區後,檔案的傳輸速度明顯提升,只需要幾十毫秒。copy速度最快的是第三種方法,我們自定義的緩衝區在1024位元組大小時,其速度略微慢於方法2,但當我們把緩衝區增加後,其速度就比方法2快了,且其記憶體區域越大,copy速度越快。因此,我們可以得出結論,最優的方案是自定義緩衝區,其次是採用java自帶緩衝區嗎,逐位元組讀取的方法我們應該儘量避免使用,效率低下。當程式反覆讀寫的次數越少,其copy速度就會越快。查閱資料可以知道java自帶的緩衝區預設是8192位元組,在第二個測試案例中我們可以發現,自定義相同大小的區域,方法3是比方法2要快的,因此,效率最高的方法依然是自定義位元組陣列作為緩衝區。