字符串串联:concat()与“ +”运算符

遇到的问题:

假设字符串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);
    }
} 

解决方案:

解决方案一

不,不是。

首先,语义上略有不同。 如果anull ,则a.concat(b)抛出NullPointerExceptiona += 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中提供了StringStringBuilder的源代码(及其程序包专用的基类)。 您会看到正在建立一个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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值