遇到的问题:
假设字符串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 += b
的原始值视为null
。 此外, concat()
方法仅接受String
值,而+
运算符将无提示地将参数转换为String
(对对象使用toString()
方法)。 因此, concat()
方法接受的条件更加严格。
若要深入了解,请编写一个带有a += b;的简单类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
方法将获胜。
Sun JDK的src.zip中提供了String
和StringBuilder
的源代码(及其程序包专用的基类)。 您会看到正在建立一个char数组(根据需要调整大小),然后在创建最终String
时将其丢弃。 实际上,内存分配出奇地快。
更新:正如Pawel Adamski指出的那样,在最近的HotSpot中,性能已经发生了变化。 javac
仍会产生完全相同的代码,但是字节码编译器会作弊。 简单的测试完全失败,因为整个代码主体都被丢弃了。 总结System.identityHashCode
(不是String.hashCode
)表明StringBuffer
代码具有一点优势。 在发布下一个更新时,或者使用其他JVM时,可能会发生更改。 来自@lukaseder 的HotSpot JVM内部函数列表 。
解决方案二
Niyaz是正确的,但也值得注意的是,特殊的+运算符可以通过Java编译器转换成更有效的格式。 Java有一个StringBuilder
类,该类表示一个非线程安全的可变String。 当执行一串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次,每次运行10万次。 结果如下:
StringBuilder
胜出。 大多数运行的时钟时间结果为0,最长的时间为16ms。a += b
每次运行大约需要40000ms(40s)。concat
每次运行只需要10000ms(10s)。
我还没有反编译该类以查看内部结构或通过事件探查器运行它,但是我怀疑a += b
花费了大量时间来创建StringBuilder
新对象,然后将它们转换回String
。
原文链接: http://findmybug.cn/article/string-concatenation-concat-vs-operator