字符串连接:concat()vs“+”运算符
假设字符串a和b:
a += b
a = a.concat(b)
在引擎盖下,他们是同一件事吗?
这里是concat作为参考反编译。 我希望能够反编译+
运算符以查看它的功能。
public String concat(String s) {
int i = s.length();
if (i == 0) {
return this;
}
else {
char ac[] = new char[count + i];
getChars(0, count, ac, 0);
s.getChars(0, i, ac, count);
return new String(0, count + i, ac);
}
}
不,不完全。
首先,语义有细微差别。 如果a
是null
,那么a.concat(b)
引发一个NullPointerException
但a+=b
会治疗的原始值a
就好像它是null
。 此外, concat()
方法只接受String
值,而+
运算符将静态地将参数转换为String(对象使用toString()
方法)。 所以concat()
方法在它接受的方面更加严格。
为了看看底下,写一个简单的类与a += b;
public class Concat {
String cat(String a, String b) {
a += b;
return a;
}
}
现在使用javap -c
(包含在Sun JDK中)进行反汇编。 你应该看到一个列表包括:
java.lang.String cat(java.lang.String, java.lang.String);
Code:
0: new #2; //class java/lang/StringBuilder
3: dup
4: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V
7: aload_1
8: invokevirtual #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: aload_2
12: invokevirtual #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: invokevirtual #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/ String;
18: astore_1
19: aload_1
20: areturn
所以, a += b
是等价的
a = new StringBuilder()
.append(a)
.append(b)
.toString();
concat
方法应该更快。 但是,使用更多字符串时, StringBuilder
方法至少在性能方面会获胜。
String
和StringBuilder
(及其包私有基类)的源代码可在Sun JDK的src.zip中找到。 你可以看到你正在建立一个char数组(根据需要调整大小),然后在创建最终String
时将其扔掉。 实际上,内存分配非常快。
更新:正如Pawel Adamski所指出的,在最近的HotSpot中,性能发生了变化。 javac
仍然会生成完全相同的代码,但字节码编译器会作弊。 简单的测试完全失败,因为整个代码都被扔掉了。 总结System.identityHashCode
(不是String.hashCode
)显示StringBuffer
代码具有轻微的优势。 如果下次更新发布,或者您使用其他JVM,可能会有所变化。 来自@lukaseder,一个HotSpot JVM内部函数列表。
Niyaz是正确的,但值得注意的是,特殊的+运算符可以被Java编译器转换为更高效的东西。 Java有一个StringBuilder类,它表示一个非线程安全的可变字符串。 在执行一串String连接时,Java编译器会自动转换
String a = b + c + d;
成
String a = new StringBuilder(b).append(c).append(d).toString();
这对于大型弦乐来说效率更高。 据我所知,当你使用concat方法时不会发生这种情况。
但是,将空字符串连接到现有字符串时,concat方法更有效。 在这种情况下,JVM不需要创建一个新的String对象,并且可以简单地返回现有的对象。 请参阅concat文档以确认这一点。
因此,如果您对效率超级关注,那么在连接可能为空的字符串时应该使用concat方法,否则使用+。 但是,性能差异应该可以忽略不计,您可能不应该担心这一点。
我运行了一个类似于@marcio的测试,但用下面的循环代替:
String c = a;
for (long i = 0; i < 100000L; i++) {
c = c.concat(b); // make sure javac cannot skip the loop
// using c += b for the alternative
}
为了更好的衡量,我也投入了StringBuilder.append()
。 每次测试运行10次,每次运行100k次。 结果如下:
StringBuilder
胜出。 大多数运行的时钟时间结果为0,最长时间为16ms。 a += b
每次运行需要大约40000ms(40s)。 concat
每次运行只需要10000ms(10s)。 我没有反编译该类以查看内部结构或通过分析器运行它,但我怀疑a += b
花费了大量时间创建StringBuilder
新对象,然后将它们转换回String
。