循环语句中为什么最好使用StringBuilder代替“+”

本文转载自他人博客,留下记录,方便自己复习,也能方便有需要的人。

for循环中为什么最好使用StringBuilder?

先看不使用StringBuilder的情况

public class Test {

    public static void testForStr(){
        String str = "";
        for (int i=0; i<10; i++){
            str += i;
        }
    }

    public static void main(String[] args){
        Test.testForStr();
    }
}

为了更好的分析编译器到底干了什么,我们需要使用javap命令进行字节码分析,终端执行以下命令:

javac Test.java
javap -c Test.class

执行结果:

Compiled from "Test.java"
public class top.freesh.laughably.Test {
  public top.freesh.laughably.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void testForStr();
    Code:
             //从常量池引用#2并推向栈顶
       0: ldc           #2                  // String abc
             //将栈顶的引用存入第一个局部变量
       2: astore_0
             //将int型常量0推入栈顶
       3: iconst_0
             //将栈顶0存入第二个int型变量中
       4: istore_1
             //第二个int局部变量进栈
       5: iload_1
             //将byte型常量10推入栈顶
       6: bipush        10
             //如果栈顶两个值大于等于0(此时0-10)则跳转36(code)
       8: if_icmpge     36
             //创建StringBuilder对象,其引用进栈
      11: new           #3                  // class java/lang/StringBuilder
            //复制栈顶引用并进栈(此时栈顶有两个对象的引用)
      14: dup
            //调用StringBuilder的构造方法(消耗掉一个对象引用)
      15: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
            //第一个引用变量入栈(第一次为:abc)
      18: aload_0
            //调用append方法,消耗一个对象引用和一个abc,并返回一个对象引用存入栈顶(注意到append()方法返回的是StringBuilder引用)
      19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            //第二个int型变量入栈(0入栈,此处对应循环体中的i)
      22: iload_1
            //调用append方法,消耗一个对象引用和o,并返回一个对象引用存入栈顶(同19)
      23: invokevirtual #6                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
            //调用toString方法,并将产生的String存入栈顶
      26: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            //将栈顶String存入本地第一个引用变量中
      29: astore_0
            //将本地第二个int型变量增加1
      30: iinc          1, 1
            //无条件跳转到5(code)
      33: goto          5
            //返回
      36: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #8                  // Method testForStr:()V
       3: return
}

可见for循环中,对字符串进行加法操作,都会产生一个StringBuilder对象,这虽然减少了jvm常量池的压力,但也无疑增加了jvm中新生代的压力(增加了垃圾回收的几率)

再来看看使用StringBuilder的情况吧

public class Test {

    public static void testForStr(){
        //和上边的代码逻辑一样,只是这里使用StringBuilder连接字符串
        StringBuilder stringBuilder = new StringBuilder("abc");
        for (int i=0; i<127; i++){
            stringBuilder.append(i);
        }
    }

    public static void main(String[] args){
        Test.testForStr();
    }
}

使用同样的命令分析jvm指令结果如下:

Compiled from "Test.java"
public class top.freesh.laughably.Test {
  public top.freesh.laughably.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void testForStr();
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup
       4: ldc           #3                  // String abc
       6: invokespecial #4                  // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
             //存储的是StringBuilder引用
       9: astore_0
      10: iconst_0
      11: istore_1
      12: iload_1
      13: bipush        10
      15: if_icmpge     30
      18: aload_0
      19: iload_1
      20: invokevirtual #5                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
            //此处将append产生的对象引用(此时位于栈顶)出栈,因为aload_0每次都会将引用入栈
      23: pop
      24: iinc          1, 1
      27: goto          12
      30: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #6                  // Method testForStr:()V
       3: return
}

完全没有问题,每次循环也不会产生StringBuilder对象了

  • 建议大家以后在循环中使用StringBuilder来操作字符串拼接工作

  • 普通代码中使用加号连接字符串会被jvm优化成使用StringBuilder拼接(感兴趣的朋友可以试一下)

发布了24 篇原创文章 · 获赞 2 · 访问量 415

猜你喜欢

转载自blog.csdn.net/qq_42107430/article/details/103873835