Java高效文件写入:字符串拼接与多次调用write()的比较

3
在下面的代码中,哪种情况(1或2)更“高效”?
static final String NEWLINE = System.getProperty("line.separator");
Vector<String> text_vec = ...;
FileWriter file_writer = new FileWriter(path);
BufferedWriter buffered_writer = new BufferedWriter(file_writer);
try {
    for (String text: text_vec) {

        // Case 1: String concatenation
        buffered_writer.write(text + NEWLINE);

        // Case 2: Extra call to write()
        buffered_writer.write(text);
        buffered_writer.write(NEWLINE);
    }
}
finally {
    buffered_writer.close();
}

就第一种情况而言,据我所知,Java编译器会自动分配一个StringBuilder对象来处理字符串连接。由于在编译时无法预知字符串的值,因此不可能在“编译期”进行连接。

那么问题来了:哪种方法更有效(CPU /内存/挂钟时间)?

关于“有效”的确切定义,我留给回答者们去解释。我不是Java虚拟机方面的专家。

4个回答

4

除非你进行了基准测试并证明你有充分的理由这样做,否则你应该直接写入缓冲区。它提供了一种高效的方法来写入文件。

另外,在写完之后不要忘记清空缓冲区。


4
据我所知,关闭操作会自动刷新底层流,因此很少需要手动刷新。 - Sanjay T. Sharma

1
第二个选项应该更快,因为它可以防止无用的字符串对象创建。这是因为BufferedWriter直接将其输入转换为字节/字符,而不是创建无用的字符串对象。
使用方案1:
您的代码执行了:
1次 StringBuilder;(通过将字符串连接在一起隐含)
1次 String;(通过将字符串连接在一起隐含)
1次 char[];由缓冲写入器
使用方案2:
您的代码执行了:
2次 char[];由缓冲写入器

由于最后一个案例减少了对象数量和大对象数量,因此应该更快。


0

文件IO比在JVM中进行的字符串拼接慢了几个数量级。无论如何,磁盘都将成为你的瓶颈。

这是在假设我们不讨论固态硬盘(SSD)的情况下,它可能仍然比JVM本身慢。

编辑:显然根据评论的回应,我表达得不够清楚。让我试试用一个更大的文本框。OP指出了一个FileWriter。这意味着最终所有数据都必须写入磁盘。磁盘延迟以毫秒为单位测量,吞吐量以兆字节/秒为单位,通常是10个左右,但不会超过100个。

内存延迟以纳秒为单位测量,吞吐量以每秒吉字节为单位。大部分字符串拼接都发生在这里。这比磁盘快3-6个数量级。

无论你如何缓冲、拼接等等,最终你都会遇到磁盘吞吐量的限制。尤其是如果你调用flush()并等待它实际完成对介质的刷新。

缓冲除了使磁盘IO更高效之外,并没有其他作用。是的,你在写入时应该使用一个缓冲区,大约1k-16k的大小就足够了。然而,这个缓冲区不会改变你的磁盘吞吐量。而磁盘的速度比字符串拼接问题慢了几个数量级。

所以,如果你想谈论写入磁盘时的效率,就不要考虑如何连接字符串。只需编写易于阅读的代码即可。


1
他不是直接写入文件,而是写入缓冲区。 - Bill the Lizard
无论数据是暂时存储在字符串还是缓冲区中,他都将写入该文件。这没有任何区别。 - Bill the Lizard
我的第一条评论的观点是,向缓冲区写入数据并不比字符串连接慢上几个数量级。 - Bill the Lizard

0

所有的数据都会经过缓冲区,所以差别应该不大。可能带有额外写入操作的版本2更快。

但是除非你要执行这个操作无数次,否则速度并不重要。


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