字符串连接真的那么慢吗?

我目前正在研究String concat选项以及它们对整体性能的惩罚。 我的测试用例创造了令我心烦意乱的结果,我不确定我是否会忽略某些东西。

这是交易:在java中执行"something"+"somethingElse" (在编译时)每次完成时都会创建一个新的StringBuilder

对于我的测试用例,我正在从我的HDD加载一个包含1661行示例数据的文件 (经典的“Lorem Ipsum”)。 这个问题不是关于I / O性能 ,而是关于不同字符串concat方法的性能。

 public class InefficientStringConcat { public static void main(String[] agrs) throws Exception{ // Get a file with example data: System.out.println("Starting benchmark"); // Read an measure: for (int i = 0; i < 10; i++){ BufferedReader in = new BufferedReader( new InputStreamReader(new FileInputStream(new File("data.txt"))) ); long start = System.currentTimeMillis(); // Un-comment method to test: //inefficientRead(in); //betterRead(in); long end = System.currentTimeMillis(); System.out.println("Took "+(end-start)+"ms"); in.close(); } } public static String betterRead(BufferedReader in) throws IOException{ StringBuilder b = new StringBuilder(); String line; while ((line = in.readLine()) != null){ b.append(line); } return b.toString(); } public static String inefficientRead(BufferedReader in) throws IOException { String everything = "", line; while ((line = in.readLine()) != null){ everything += line; } return everything; } } 

如您所见,两个测试的设置相同。 结果如下:

使用inefficientRead() – 方法

 Starting benchmark #1 Took 658ms #2 Took 590ms #3 Took 569ms #4 Took 567ms #5 Took 562ms #6 Took 570ms #7 Took 563ms #8 Took 568ms #9 Took 560ms #10 Took 568ms 

使用betterRead() – 方法

 Starting benchmark #1 Took 42ms #2 Took 10ms #3 Took 5ms #4 Took 7ms #5 Took 16ms #6 Took 3ms #7 Took 4ms #8 Took 5ms #9 Took 5ms #10 Took 13ms 

我正在运行测试, 没有额外的参数java -command。 我从2009年初开始运行MacMini3,1和Sun JDK 7:

 [luke@BlackBox ~]$ java -version java version "1.7.0_09" Java(TM) SE Runtime Environment (build 1.7.0_09-b05) Java HotSpot(TM) Client VM (build 23.5-b02, mixed mode) 

这让我觉得非常沉重。 我在测量这个时做错了什么,或者这应该发生?

我在测量这个时做错了什么,或者这应该发生?

它应该发生。 使用重复的字符串连接构造长字符串是一种已知的性能反模式: 每个连接都必须创建一个新字符串,其中包含原始字符串的副本以及附加字符串的副本 。 你最终获得了O(N 2 )的表现。 当您使用StringBuilder ,大多数情况下您只是将附加字符串复制到缓冲区中。 有时,缓冲区需要耗尽空间并需要扩展(通过将现有数据复制到新缓冲区中),但这种情况不会经常发生(由于缓冲区扩展策略)。

有关详细信息,请参阅我关于字符串连接的文章 – 这是一篇非常古老的文章,因此早于StringBuilder ,但基础知识没有改变。 (基本上StringBuilder就像StringBuffer ,但没有同步。)

这正是应该发生的事情。 betterRead需要线性时间; inefficientRead需要二次时间。