字符串连接你用+还是用StringBuilder

撸了今年阿里、腾讯和美团的面试,我有一个重要发现…….

作者:超人汪小建(seaboat)

出处:https://blog.csdn.net/wangyangzhizhou/column/info/16032


前言

据我所知字符串确实已经成为 Java 开发人员最常用的类了,而且是大量使用。我们都知道,String 其实是封装了字符,所以俩字符串连接就是将字符串对象里面的字符连起来。很多人习惯使用+来连接字符串,也有人会用 StringBuilder 的append方法。

“+”编译后

看看如果我们在程序中直接使用+来连接字符串的情况,用下面一个简单的例子来说明,进行两个字符串连接操作,即s3 = s1 + s2

    public class TestString {
        public static void main(String[] args) {
            String s1 = "www";
            String s2 = "ccc";
            String s3 = s1 + s2;
        }
    }

接着javap -c TestString.class看一下编译后的情况,可以看到编译器其实是对+进行了转换的,转成了 StringBuilder 对象来操作了,首先使用 s1 创建 StringBuilder 对象,然后用 append方法连接 s2,最后调用toString方法完成。

    public class com.seaboat.string.TestString {
      public com.seaboat.string.TestString();
        Code:
           0: aload_0
           1: invokespecial #8                  // Method java/lang/Object."<init>":()V
           4: return

      public static void main(java.lang.String[]);
        Code:
           0: ldc           #16                 // String www
           2: astore_1
           3: ldc           #18                 // String ccc
           5: astore_2
           6: new           #20                 // class java/lang/StringBuilder
           9: dup
          10: aload_1
          11: invokestatic  #22                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
          14: invokespecial #28                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
          17: aload_2
          18: invokevirtual #31                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          21: invokevirtual #35                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          24: astore_3
          25: return
    }

“+”与”append”等价吗

前面可以看到+在编译器作用下都会转成 StringBuilder 的append方法执行,所以如果抛开运行效率来说,它们其实本质是一样的。

本质一样是否就能说明它们时等价的呢?或者说能否为了方便直接用+来连接字符串,剩下的事就交给编译器了?继续看个例子,在这个例子中有个 for 循环进行字符串连接操作。

    public class TestString2 {
        public static void main(String[] args) {
            String s = "www";
            for (int i = 0; i < 10; i++)
                s += i;
        }
    }

编译后的情况如下,不熟悉指令没关系,我们只看重要的部分,if_icmplt 8,这个就是 for 循环的条件判断,小于10则不断跳到8的位置,8后面其实就是创建 StringBuilder 对象,并以本地变量s的值初始化该对象,接着再将本地变量i append到 StringBuilder 对象中,最后调用toString方法将所得值存到本地变量s。

这样来看循环中每次都要创建 StringBuilder 对象,而且要调用toString方法,这样的执行效率显然比较低,而且增加了 GC 的压力。

    public class com.seaboat.string.TestString2 {
      public com.seaboat.string.TestString2();
        Code:
           0: aload_0
           1: invokespecial #8                  // Method java/lang/Object."<init>":()V
           4: return

      public static void main(java.lang.String[]);
        Code:
           0: ldc           #16                 // String www
           2: astore_1
           3: iconst_0
           4: istore_2
           5: goto          30
           8: new           #18                 // class java/lang/StringBuilder
          11: dup
          12: aload_1
          13: invokestatic  #20                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
          16: invokespecial #26                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
          19: iload_2
          20: invokevirtual #29                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
          23: invokevirtual #33                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          26: astore_1
          27: iinc          2, 1
          30: iload_2
          31: bipush        10
          33: if_icmplt     8
          36: return
    }

友好写法

把事情都丢给编译器是不友好的,为了能让程序执行更加高效,最好是我们自己来控制 StringBuilder 的实例,比如下面,只创建一个 StringBuilder 实例,后面用append方法连接字符串。

    public class TestString3 {
        public static void main(String[] args) {
            StringBuilder sb = new StringBuilder("www");
            for (int i = 0; i < 10; i++)
                sb.append(i);
        }
    }

编译后的情况如下,首先创建一个 StringBuilder 对象,使用字符串”www”来实例化该对象,接着循环调用append方法将本地变量i添加到 StringBuilder 对象中。

    public class com.seaboat.string.TestString3 {
      public com.seaboat.string.TestString3();
        Code:
           0: aload_0
           1: invokespecial #8                  // Method java/lang/Object."<init>":()V
           4: return

      public static void main(java.lang.String[]);
        Code:
           0: new           #16                 // class java/lang/StringBuilder
           3: dup
           4: ldc           #18                 // String www
           6: invokespecial #20                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
           9: astore_1
          10: iconst_0
          11: istore_2
          12: goto          24
          15: aload_1
          16: iload_2
          17: invokevirtual #23                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
          20: pop
          21: iinc          2, 1
          24: iload_2
          25: bipush        10
          27: if_icmplt     15
          30: return
    }
赞(1) 打赏

如未加特殊说明,此网站文章均为原创,转载必须注明出处。Java 技术驿站 » 字符串连接你用+还是用StringBuilder
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

关注【Java 技术驿站】公众号,每天早上 8:10 为你推送一篇技术文章

扫描二维码关注我!


关注【Java 技术驿站】公众号 回复 “VIP”,获取 VIP 地址永久关闭弹出窗口

免费获取资源

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏