前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >017:为什么不建议在循环中使用“+”拼接字符串

017:为什么不建议在循环中使用“+”拼接字符串

作者头像
阿杜
发布2019-03-12 16:49:29
1.2K0
发布2019-03-12 16:49:29
举报
文章被收录于专栏:阿杜的世界阿杜的世界

典型答案

由于字符串对象是不可变的,所以每次循环都会对操作符左右两边的字符串进行拷贝,并生成一个新的字符串对象。如果循环n次,则这个过程需要n的平方级的时间;并且在这个过程中还创建了很多短命的中间对象。

如果要使用循环构建一个大的字符串,推荐使用StringBuilder代替String,使用StringBuilder的append()方法进行字符串连接,并在循环结束后将StringBuilder对象转为String对象。StringBuilder的原理是预先分配了一个足够大小的缓冲区,然后循环的过程就是往缓冲区里填充数据,比使用“+”做字符串连接的效率要高很多。

知识点梳理

上面的答案是理论知识,这里看下实际案例,假设有如下代码,循环10000次将随机长度80的字符串连接为一个大的字符串,使用“+”和使用StringBuilder的方法之间的差距是两个数量级(我的环境:Mac OS 16G + JDK 1.8)

代码语言:javascript
复制
public class StringConcatExample {

    public static void main(String[] args) {
        long s1 = System.currentTimeMillis();
        new StringConcatExample().implicit();
        System.out.println("使用\"+\"拼接:" + (System.currentTimeMillis() - s1));

        s1 = System.currentTimeMillis();
        new StringConcatExample().exlicit();
        System.out.println("使用\"StringBuilder\"拼接:" + (System.currentTimeMillis() - s1));
    }

    public String implicit() {
        String result = "";
        for (int i = 0; i < 10000; i++) {
            result += (i + ",");
        }
        return result;
    }

    public String exlicit() {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < 10000; i++) {
            result.append(i).append(",");
        }
        return result.toString();
    }
}

上述代码的运行结果如下:

WechatIMG22.jpeg

使用javac StringConcatExample.java命令编译源文件,使用javap -c StringConcatExample命令查看字节码文件的内容。

implicit()方法的字节码:

代码语言:javascript
复制
  public java.lang.String implicit();
    Code:
       0: ldc           #16                 // String
       2: astore_1
       3: iconst_0
       4: istore_2
       5: iload_2
       6: sipush        10000
       9: if_icmpge     42
      12: new           #7                  // class java/lang/StringBuilder
      15: dup
      16: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
      19: aload_1
      20: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: iload_2
      24: invokevirtual #17                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      27: ldc           #18                 // String ,
      29: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      32: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      35: astore_1
      36: iinc          2, 1
      39: goto          5
      42: aload_1
      43: areturn

可以看出,第9行到第39行构成了一个循环体:在第9行的时候做条件判断,如果不满足循环条件,则跳转到42行。编译器做了一定程度的优化,在12行new了一个StringBuilder对象,然后再20行、24行、29进行了三次append方法的调用,不过重点是,每次循环都会new一个StringBuilder对象。

再看explicit()方法的字节码,如下所示:

代码语言:javascript
复制
 public java.lang.String exlicit();
    Code:
       0: new           #7                  // class java/lang/StringBuilder
       3: dup
       4: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
       7: astore_1
       8: iconst_0
       9: istore_2
      10: iload_2
      11: sipush        10000
      14: if_icmpge     34
      17: aload_1
      18: iload_2
      19: invokevirtual #17                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      22: ldc           #18                 // String ,
      24: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      27: pop
      28: iinc          2, 1
      31: goto          10
      34: aload_1
      35: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      38: areturn

14行到31行构成了循环体,可以看出,在第4行(循环体外)就构建好了StringBuilder对象,然后再循环体内只进行append()方法的调用。这就从字节码层面解释了为什么不建议在循环体内使用“+”执行字符串的拼接。

参考资料

  1. 《Effective Java(第二版)》
  2. 《Java编程思想》
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.03.03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 典型答案
  • 知识点梳理
  • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档