在第一步,我运行此代码:
public class Demo {
public static void main(String[] args) {
String x = "x";
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++)
{
x = x.concat("s");
// x+="k";
}
System.out.println(System.currentTimeMillis() - start);
}
}
出:13579。
第二步,我运行此代码:
public class Demo {
public static void main(String[] args) {
String x = "x";
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++)
{
//x = x.concat("s");
x+="k";
}
System.out.println(System.currentTimeMillis() - start);
}
}
出:27328。
我有两个问题:
答案 0 :(得分:6)
我认为你的microbenchmark很好,我可以重现你的结果。
在我的JVM上,x += "k"
速度慢两倍的原因是它覆盖了以下内容:
StringBuilder
; x
附加到StringBuilder
; "k"
附加到StringBuilder
; StringBuilder.toString()
并将结果分配给x
。这会复制两次字符数据(一次在步骤2中,一次在步骤4中)。
另一方面,x = x.concat("s")
仅复制一次数据。
这种双重复制使x += "k"
比另一个版本慢两倍。
如果您感到好奇,以下是我的编译器为+=
循环生成的字节码:
10: goto 36
13: new #24; //class java/lang/StringBuilder
16: dup
17: aload_1
18: invokestatic #26; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
21: invokespecial #32; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
24: ldc #35; //String k
26: invokevirtual #37; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: invokevirtual #41; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
32: astore_1
33: iinc 4, 1
36: iload 4
38: ldc #45; //int 100000
40: if_icmplt 13
说明21&amp; 29是制作两份副本的地方。
答案 1 :(得分:0)
因为当您使用+
连接字符串时,实际上创建了一个新的StringBuffer并使用它而不是原始字符串,这就是为什么最终可能比只使用concat时更慢。
关于正确性,最好查看字节码以查看实际情况。如果编译器知道其执行的确切结果,它可能会优化掉一些代码,而且它永远不会改变。
答案 2 :(得分:0)
现在试试这个,它会击败你们两个:
public class Demo {
public static void main(String[] args) {
StringBuilder x = new StringBuilder("x");
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++)
{
x.append("k");
}
System.out.println(System.currentTimeMillis() - start);
}
}
这是一个稍微优化的版本:
public class Demo {
public static void main(String[] args) {
StringBuilder x = new StringBuilder(100001).append('x');
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++)
{
x.append('k');
}
System.out.println(System.currentTimeMillis() - start);
}
}
答案 3 :(得分:0)
如果你知道这些是等价的,那会有用吗?
x+="k";
x = new StringBuffer(x).append("k").toString();
或者在较新的Java中(我忘记它是从1.5还是1.6)它改为使用它,因为它至少避免了StringBuffer
对它造成的锁定:
x = new StringBuilder(x).append("k").toString();
随着所有内存管理的继续,毫无疑问concat(String)
方法可以做得更好。另一方面,如果你要做那个循环,你最好不要在可变对象和不可变对象之间来回转换:
String x = "x";
long start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder(x);
for (int i = 0; i < 100000; i++) {
sb.append("f");
}
x = sb.toString();
System.out.println(System.currentTimeMillis() - start);
由于StringBuilder
(和StringBuffer
一样,但由于其他原因而效率较低)为您做智能缓冲管理,这将是一种明显更快的技术;比这更快的速度需要真正的努力(或至少预先调整缓冲区的大小,这在这种情况下是可能的,但一般来说更难)。
答案 4 :(得分:0)
如果您单步执行
等行 String x2 = x + "x";
在Eclipse中,您将看到它创建了一个StringBuilder
帮助构造新String
的对象。那是什么
发生在你的第二个案件中。
在第一种情况下,String.concat
直接创建新的String
任何StringBuilder
所以它会快一点。