壓縮檔案,IO拷貝優化。
阿新 • • 發佈:2021-10-27
public class TestIO { public static void main(String[] args) { long beginTime = System.currentTimeMillis(); String ZIP_FILE = "1023.zip"; File zipFile = new File(ZIP_FILE); File src = new File("F:\\person_code\\jvm_deep_base\\module04\\src\\main\\java\\jdk1.8.0_291.zip"); try (// 在這裡宣告資源,jdk1.7以後會自動關閉關閉資源 ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile)); InputStream inputStream = new FileInputStream(src);) { zipOut.putNextEntry(new ZipEntry("1023.zip")); int trans = 0; while ((trans = inputStream.read()) != -1) { zipOut.write(trans); } } catch (Exception e) { e.printStackTrace(); } System.out.println("檔案大小: " + src.length()); System.out.println("耗時: " + (System.currentTimeMillis() - beginTime)); } }
使用位元組輸入輸出流進行I/O拷貝,傳輸單位是一個位元組,速度緩慢。
public class TestIO2 { public static void main(String[] args) { String ZIP_FILE = "1023.zip"; File zipFile = new File(ZIP_FILE); File src = new File("F:\\person_code\\jvm_deep_base\\module04\\src\\main\\java\\jdk1.8.0_291.zip"); long beginTime = System.currentTimeMillis(); try ( // jdk以後 這裡宣告的變數會自動關閉申請的資源 ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile)); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(zipOut); BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(src)); ) { zipOut.putNextEntry(new ZipEntry("1023.zip")); int trans = 0; while ((trans = bufferedInputStream.read()) != -1) { bufferedOutputStream.write(trans); } } catch (Exception e) { e.printStackTrace(); } System.out.println("檔案大小: " + src.length()); System.out.println("耗時: " + (System.currentTimeMillis() - beginTime)); } }
使用位元組緩衝輸入輸出流進行I/O拷貝,傳輸單位是一個位元組,速度緩慢。例如我們現在有30000個位元組的資料,如果使用 FileInputStream
那麼就需要呼叫30000次的本地方法來獲取這些資料,而如果使用緩衝區的話(這裡假設初始的緩衝區大小足夠放下30000位元組的資料)那麼只需要呼叫一次就行。
public class TestIO3 { public static void main(String[] args) { String ZIP_FILE = "1023.zip"; File zipFile = new File(ZIP_FILE); File src = new File("F:\\person_code\\jvm_deep_base\\module04\\src\\main\\java\\jdk1.8.0_291.zip"); long beginTime = System.currentTimeMillis(); try ( // jdk以後 這裡宣告的變數會自動關閉申請的資源 ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile)); WritableByteChannel writableByteChannel = Channels.newChannel(zipOut); FileChannel channel = new FileInputStream(src).getChannel(); ) { zipOut.putNextEntry(new ZipEntry("1023.zip")); ByteBuffer buffer = ByteBuffer.allocate(1024); while (channel.read(buffer) != -1) { buffer.flip(); writableByteChannel.write(buffer); buffer.clear(); } } catch (Exception e) { e.printStackTrace(); } System.out.println("檔案大小: " + src.length()); System.out.println("耗時: " + (System.currentTimeMillis() - beginTime)); } }
NIO中新出了 Channel
和 ByteBuffer
。正是因為它們的結構更加符合作業系統執行I/O的方式,所以其速度相比較於傳統IO而言速度有了顯著的提高。Channel
就像一個包含著煤礦的礦藏,而 ByteBuffer
則是派送到礦藏的卡車。也就是說我們與資料的互動都是與 ByteBuffer
的互動。
public class TestIO4 { public static void main(String[] args) { String ZIP_FILE = "1023.zip"; File zipFile = new File(ZIP_FILE); File src = new File("F:\\person_code\\jvm_deep_base\\module04\\src\\main\\java\\jdk1.8.0_291.zip"); long beginTime = System.currentTimeMillis(); try ( // jdk以後 這裡宣告的變數會自動關閉申請的資源 ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile)); WritableByteChannel writableByteChannel = Channels.newChannel(zipOut); FileChannel channel = new FileInputStream(src).getChannel(); ) { zipOut.putNextEntry(new ZipEntry("1023.zip")); channel.transferTo(0, src.length(), writableByteChannel); } catch (Exception e) { e.printStackTrace(); } System.out.println("檔案大小: " + src.length()); System.out.println("耗時: " + (System.currentTimeMillis() - beginTime)); } }
使用 transferTo
的效率比迴圈一個 Channel
讀取出來然後再迴圈寫入另一個 Channel
好。作業系統能夠直接傳輸位元組從檔案系統快取到目標的 Channel
中,而不需要實際的 copy
階段。簡單來說省去了從核心空間轉到使用者空間的一個過程。關於零拷貝可以參考這篇部落格。