我做了一次性能测试
String test="";
Date st = new Date();
logger.info("start "+(new Date()).toString());
for(int i = 0;i<100000;i++) {
test += "test";
}
logger.info("end "+(new Date()).toString());
test="";
StringBuilder sb = new StringBuilder(test);
logger.info("start Sb "+(new Date()).toString());
for(int i = 0;i<100000;i++) {
sb.append("test");
}
test = sb.toString();
logger.info("end sb "+(new Date()).toString());
,结果是
start Fri Jun 30 10:34:42 KRAT 2017
end Fri Jun 30 10:34:55 KRAT 2017
start Sb Fri Jun 30 10:34:55 KRAT 2017
end sb Fri Jun 30 10:34:55 KRAT 2017
差异是13秒!!!
但在任何地方我都读到口号“+”运算符更快,因为自java 1.6以来它已经过优化。怎么了?哪里弄错了?
答案 0 :(得分:2)
+通过分配StringBuilder并调用append来实现。以下代码行是等效的:
String s = new StringBuilder().append(string1).append(string2).append(string3).toString();
String s = string1 + string2 + string3;
不幸的是,编译器并不擅长在循环中识别String连接链,因此以下代码段也是等效的。
String test="";
for(int i = 0;i<100000;i++) {
test += "test";
}
String test="";
for(int i = 0;i<100000;i++) {
test = new StringBuilder().append(test).append("test").toString();
}
请注意,后者在每次循环迭代中都会分配一个新的StringBuilder,并且每次都会对结果调用toString。您可以通过查看&#39; javac Test.java&amp;&amp ;;生成的字节码来验证这一点。 javap -c测试&#39;。根据经验,如果您的字符串连接可以写成一个表达式,那么首选+运算符以便于阅读。
关于编写这些基准的附注: 您测试每个替代方案的顺序会影响其性能。您应该为每个版本编写两个单独的主要方法,以便您的测试是独立的。此外,您使用名为test的变量进行了大量工作,但未在任何地方使用它。编译器可以自由地优化你的第一个for循环,因为之后会立即重置test的值。
答案 1 :(得分:0)
&#34; +&#34;普通字符串上的运算符比&#34;追加&#34;慢得多。 StringBuilder上的操作。渐渐地,&#34; +&#34;普通字符串上的运算符具有O(n*m)
时间复杂度,其中n是旧字符串的大小,m是要追加的字符串的大小,而&#34;追加&#34; StringBuilder上的操作只有O(m)
复杂度。
<强>阐释:强>
字符串是不可变的。因此,每次执行test + "test"
时,都会创建一个新字符串,并将测试字符串的所有字符附加到新字符串,然后是&#34; test&#34;的所有字符。因此,附加的总字符数为test.length() + "test".length()
。
StringBuilder是可变的。因此,每次执行sb.append("test)
时,只会出现&#34; test&#34;被附加到现有的&#34; sb&#34;。因此,附加的总字符数为"test".length()
。