Java實現檔案拷貝的4種方法.

y_keven發表於2013-08-16

使用 java 進行檔案拷貝 相信很多人都會用,,不過效率上是否最好呢?
最近看了看NIO決定試一試 java  NIO 到底有什麼效能的提升.

第一種方法:古老的方式

 public static long forJava(File f1,File f2) throws Exception{
  
long time=new Date().getTime();
  
int length=2097152;
  FileInputStream in
=new FileInputStream(f1);
  FileOutputStream out
=new FileOutputStream(f2);
  
byte[] buffer=new byte[length];
  
while(true){
   
int ins=in.read(buffer);
   
if(ins==-1){
    in.close();
    out.flush();
    out.close();
    
return new Date().getTime()-time;
   }
else
    out.write(buffer,
0,ins);
  }
 }

方法的2引數分別是原始檔案,和拷貝的目的檔案.這裡不做過多介紹.

實現方法很簡單,分別對2個檔案構建輸入輸出流,並且使用一個位元組陣列作為我們記憶體的快取器, 然後使用流從f1 中讀出資料到快取裡,在將快取資料寫到f2裡面去.這裡的快取是2MB的位元組陣列

第2種方法:使用NIO中的管道到管道傳輸

    public static long forTransfer(File f1,File f2) throws Exception{
        
long time=new Date().getTime();
        
int length=2097152;
        FileInputStream in
=new FileInputStream(f1);
        FileOutputStream out
=new FileOutputStream(f2);
        FileChannel inC
=in.getChannel();
        FileChannel outC
=out.getChannel();
        
int i=0;
        
while(true){
            
if(inC.position()==inC.size()){
                inC.close();
                outC.close();
                
return new Date().getTime()-time;
            }
            
if((inC.size()-inC.position())<20971520)
                length
=(int)(inC.size()-inC.position());
            
else
                length
=20971520;
            inC.transferTo(inC.position(),length,outC);
            inC.position(inC.position()
+length);
            i
++;
        }
    }

實現方法:在第一種實現方法基礎上對輸入輸出流獲得其管道,然後分批次的從f1的管道中像f2的管道中輸入資料每次輸入的資料最大為2MB

方法3:記憶體檔案景象寫(讀檔案沒有使用檔案景象,有興趣的可以回去試試,,我就不試了,估計會更快)

    public static long forImage(File f1,File f2) throws Exception{
        
long time=new Date().getTime();
        
int length=2097152;
        FileInputStream in
=new FileInputStream(f1);
        RandomAccessFile out
=new RandomAccessFile(f2,"rw");
        FileChannel inC
=in.getChannel();
        MappedByteBuffer outC
=null;
        MappedByteBuffer inbuffer
=null;
        
byte[] b=new byte[length];
        
while(true){
            
if(inC.position()==inC.size()){
                inC.close();
                outC.force();
                out.close();
                
return new Date().getTime()-time;
            }
            
if((inC.size()-inC.position())<length){
                length
=(int)(inC.size()-inC.position());
            }
else{
                length
=20971520;
            }
            b
=new byte[length];
            inbuffer
=inC.map(MapMode.READ_ONLY,inC.position(),length);
            inbuffer.load();
            inbuffer.get(b);
            outC
=out.getChannel().map(MapMode.READ_WRITE,inC.position(),length);
            inC.position(b.length
+inC.position());
            outC.put(b);
            outC.force();
        }
    }

實現方法:跟傷2個例子不一樣,這裡寫檔案流沒有使用管道而是使用記憶體檔案對映(假設檔案f2在記憶體中).在迴圈中從f1的管道中讀取資料到位元組陣列裡,然後在像記憶體對映的f2檔案中寫資料.

第4種方法:管道對管道

    public static long forChannel(File f1,File f2) throws Exception{
        
long time=new Date().getTime();
        
int length=2097152;
        FileInputStream in
=new FileInputStream(f1);
        FileOutputStream out
=new FileOutputStream(f2);
        FileChannel inC
=in.getChannel();
        FileChannel outC
=out.getChannel();
        ByteBuffer b
=null;
        
while(true){
            
if(inC.position()==inC.size()){
                inC.close();
                outC.close();
                
return new Date().getTime()-time;
            }
            
if((inC.size()-inC.position())<length){
                length
=(int)(inC.size()-inC.position());
            }
else
                length
=2097152;
            b
=ByteBuffer.allocateDirect(length);
            inC.read(b);
            b.flip();
            outC.write(b);
            outC.force(
false);
        }
    }

這裡實現方式與第3種實現方式很類似,不過沒有使用記憶體影射.

 

下面是對49.3MB的檔案進行拷貝的測試時間(毫秒)

Start Copy File...  file size:50290KB
CopyFile:b1.rmvb mode:forChannel  RunTime:3203
CopyFile:b1.rmvb mode:forImage  RunTime:3328
CopyFile:b1.rmvb mode:forJava  RunTime:2172
CopyFile:b1.rmvb mode:forTransfer RunTime:1406
End Copy File!

解釋: 在測試結果中看到 古老方式,和管道向管道傳輸是最快的,,,,,為什麼呢?

我分析是這樣的,由於另外2種方法內部都使用了 位元組陣列作為快取中轉,在加上NIO內部有一個貼近系統的快取區,這無意就增加了另一個快取器,所以相對於這2個方法就要慢許多,,如果不使用 位元組陣列作為資料中轉的話相信速度會更快的..

不過比較驚訝的是 管道向管道傳輸的速度還是真挺嚇人,,, 

我的機器是 IDE硬碟120G 硬碟快取2MB, 記憶體1GB, CPU AMD2800+

相關文章