首选的使用Java ZipOutputStream和BufferedOutputStream的方法

67

在Java中,我先实例化 ZipOutputStream 还是先实例化BufferedOutputStream是有影响的吗?例如:

FileOutputStream dest = new FileOutputStream(file);
ZipOutputStream zip = new ZipOutputStream(new BufferedOutputStream(dest));

// use zip output stream to write to
或者:
FileOutputStream dest = new FileOutputStream(file);
BufferedOutputStream out = new BufferedOutputStream(new ZipOutputStream(dest));

// use buffered stream to write to

在我进行的非科学定时测试中,我似乎无法注意到什么明显区别。在Java API中,我没有找到任何关于这些方式哪个是必需或首选的说明。有什么建议吗?似乎先压缩输出,然后将其缓冲以进行写入会更有效率。


2
理论上,先压缩再缓冲会更快。然而,GZipOutputStream有一个内部缓冲区,因此它不会将单个字节写入底层流中。根据底层流类型(例如,文件与套接字)和缓冲区的相对大小,您可能会看到差异,也可能不会看到任何差异。 - parsifal
2个回答

106

您应该始终将BufferedOutputStreamZipOutputStream一起使用,而不是相反。请参见下面的代码:

FileOutputStream fos = new FileOutputStream("hello-world.zip");
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos);

try {
    for (int i = 0; i < 10; i++) {
        // not available on BufferedOutputStream
        zos.putNextEntry(new ZipEntry("hello-world." + i + ".txt"));
        zos.write("Hello World!".getBytes());
        // not available on BufferedOutputStream
        zos.closeEntry();
    }
}
finally {
    zos.close();
}

正如评论所说,BufferedOutputStream上没有可用的putNextEntry()closeEntry()方法。如果不调用这些方法,ZipOutputStream会抛出异常java.util.zip.ZipException: no current ZIP entry

为了完整起见,值得注意的是finally子句只对ZipOutputStream调用close()。这是因为按照惯例,所有内置的Java输出流包装器实现都会传播关闭操作。

编辑

我刚测试了另一种方法。结果发现,将ZipOutputStreamBufferedOutputStream结合使用,仅对其进行write()调用(而不创建/关闭条目)不会引发ZipException异常。相反,生成的ZIP文件将损坏,其中没有任何条目。


3
那么,在这种情况下,缓存有任何意义吗?我不是在争论,只是好奇是否已经有人进行过检查。 - wst
3
正如您可以在 MrSmith42 的回答 的第一部分中看到的那样,使用内部的 BufferedOutputStream 可能会带来潜在的好处,即在写入磁盘之前对已压缩的输出流进行缓冲。虽然会使用更多的内存(将 zip 压缩字节保留在内存缓冲区中,直到刷新到磁盘),但效率更高,因为磁盘 I/O 是以较大块的字节进行的(与初始化 BufferedOutputStream 的缓冲区大小相同)。 - Daniel Dinnyes
你应该自己 figure out 使用 ZipOutputStream 中的 buffer size BufferedOutputStream 最适合你的性能。 - Daniel Dinnyes

24

你应该:

ZipOutputStream out =  new ZipOutputStream(new BufferedOutputStream(dest));

因为您希望将写入磁盘的操作缓冲处理(因为在大数据块中这比在许多小数据块中更有效率)。


这样做

new BufferedOutputStream(new ZipOutputStream(dest));

在进行zip压缩之前需要缓冲。但这一切都在内存中完成,不需要缓冲,因为许多小的内存访问与几个大的内存访问速度大致相同。

通常来说,在内存中所需的时间与读/写的字节数成比例。

正如评论中提到的:

ZipOutputStream 的方法如果不是 BufferedOutputStream 的一部分,则也不可用。例如,putNextEntrycloseEntry


1
我相信我的答案是正确的。但是请随意尝试两种方式并比较它们的性能(或调试它们)。 - MrSmith42
8
我的观点是,没有比较两者之间任何性能的意义。将ZipOutputStream包装在BufferedOutputStream中完全是没有意义的,因为它不会暴露putNextEntrycloseEntry方法。 - Daniel Dinnyes
2
下降投票,因为答案没有提到如果包装方式错误,则在流上时ZipOutputStream的方法不可用的事实。 - Christoffer Soop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接