深度透析String与StringBuilder

String的不可变性带来的一定效率问题。

public class Concatenation {
     public static void main(String[] args) {
         String mango="mango";
         String s="abc"+mango+"def"+47;
         System.out.println(res);
     }
}

这种“+”会产生一大堆需要垃圾回收的中间对象。可以通过jdk自带的javap来反编译以上代码,就知道以上代码如何工作的。(为关注有效部分,JVM字节码经过有效简化,下同)

D:\Download>javap -c Concatenation.class
  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String mango
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      10: ldc           #5                  // String abc
      12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      15: aload_1
      16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: ldc           #7                  // String def
      21: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: bipush        47
      26: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      29: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      32: astore_2
      33: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
      36: aload_2
      37: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      40: return
}

这-c标志表示将生成JVM字节码,其中dup和invokevirtual语句相当于Java虚拟机上的汇编语句,可以看出重点是:编译器自动引入了java.lang.StringBuilder类。虽然我们源代码总没有加入StringBuilder类,因为它高效,编译器自动使用了它。从上边看出编译器创建了一个StringBuilder对象,用以构建最终的String对象,并为每个字符串调用一次StringBuilider的append()方法,总计4次。最后调用StringBuilder的toString()方法生成结果,并存为s(使用的命令是astore_2)。
要有对比才能看出性能的区别,

public class WhitherStringBuilder {
     public String implicit(String[] fields) {
         String result = "";
         for (int i = 0; i < fields.length; i++) {
              result += fields[i];
         }
         return result;
     }
     public String explicit(String[] fields) {
         StringBuilder result = new StringBuilder();
         for (int i = 0; i < fields.length; i++) {
              result.append(fields[i]);
         }
         return result.toString();
     }
}
D:\Download>javap -c WhitherStringBuilder.class
 public java.lang.String implicit(java.lang.String[]);
    Code:
       0: ldc           #2                  // String
       2: astore_2
       3: iconst_0
       4: istore_3
       5: iload_3
       6: aload_1
       7: arraylength
       8: if_icmpge     38
      11: new           #3                  // class java/lang/StringBuilder
      14: dup
      15: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      18: aload_2
      19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: aload_1
      23: iload_3
      24: aaload
      25: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      28: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      31: astore_2
      32: iinc          3, 1
      35: goto          5
      38: aload_2
      39: areturn

  public java.lang.String explicit(java.lang.String[]);
    Code:
       0: new           #3                  // class java/lang/StringBuilder
       3: dup
       4: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
       7: astore_2
       8: iconst_0
       9: istore_3
      10: iload_3
      11: aload_1
      12: arraylength
      13: if_icmpge     30
      16: aload_2
      17: aload_1
      18: iload_3
      19: aaload
      20: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: pop
      24: iinc          3, 1
      27: goto          10
      30: aload_2
      31: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      34: areturn
}

首先看implicit()方法,第8行到第35行构成一个循环体。第8行:队堆栈中的操作数进行“大于或等于的整数比较运算”,循环结束时跳到第38行。第35行:返回循环体的起始点(第5行)。可以看到StringBuilder在循环体内构造,这就意味着没经过一次循环就会产生一个新的StringBuilder对象。
再来看explicit()方法,这个方法的不仅循环代码更简短,更简单,而且它只生成了一个StringBuilder对象。同时显式地创建StringBuilder还允许预先为其指定大小,准确知道大小,预先分好,避免多次重新分配缓冲。
因此,字符串比较简单,可使用String类,要是比较复杂或在循环体内使用建议使用StringBuilder类。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值