压缩文件,IO拷贝优化。
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
阶段。简单来说省去了从内核空间转到用户空间的一个过程。关于零拷贝可以参考这篇博客。地址是:https://www.cnblogs.com/ykpkris/articles/15472699.html