Java后端

字符串拼接的三种方式

三种方式:1. “+”操作符;2.StringBuilder;3.StringBuffer
StringBuffer线程安全,StringBuilder非线程安全;为什么会这样呢?话不多说,直接上源码:

//StringBuffer的append方法
public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
}
//StringBuilder的append方法
public StringBuilder append(int i) {
        super.append(i);
        return this;
}

从截取的部分源码中可以看到StringBuffer的append方法通过关键词synchronized修饰,StringBuilder的append方法并没有任何用来保证安全的操作;结论显而易见:StringBuffer线程安全,StringBuilder非线程安全。线程安全意味着消耗更多的性能,在效率上StringBuilder高于StringBuffer。

由于在实际项目中,非线程安全的字符串拼接应用较多一些,因此重点说下 “+”操作符StringBuilder的区别(最重要的原因还是网上有很多关于 “+”操作符StringBuilder的区别的说法让人云里雾里,解答不尽如人意)。
我们先通过现象看本质,话不多说,来个测试代码:

public static void main(String[] args) {
        final int num = 1000;
        long start = System.currentTimeMillis();
        String str = "";
        for (int i = 0; i < num; i++) {
            str = str + i;
        }
        long center = System.currentTimeMillis();
        StringBuilder b = new StringBuilder();
        for (int i = 0; i < num; i++) {
            b.append(i);
        }
        System.out.println("+操作符所花时间:" + (center - start) +
                "ms;StringBuilder拼接所花时间:" + (System.currentTimeMillis() - center) + "ms");
    }

运行结果:+操作符所花时间:7ms;StringBuilder拼接所花时间:0ms;由于运行机器等的原因,运行结果不一定相同,但肯定大同小异(num的值不宜设置过小,太小啦时间差异难以体现)。难道StringBuilder拼接的运行效率要高于+操作符的拼接效率?其实不然,从源码的角度很难说清,既然如此,那就只能从更底层的东西说起了,话不多说,直接上代码:

//测试代码
 public static void main(String[] args) {
        new Test1().m1();
        new Test1().m2();
    }
    public void m1() {
        String str = "";
        for (int i = 0; i < 5; i++) {
            str = str + i;
        }
        System.out.println(str);
    }
    public void m2() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 5; i++) {
            sb.append(i);
        }
        System.out.println(sb.toString());
    }

javap命令:

javap -v Test1.class > Test1.txt

通过javap命令生成的字节码文件Test1.txt(代码一样,产生的字节码文件也会一样,截取部分重要的):

public void m1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #6                  // String
         2: astore_1
         3: iconst_0
         4: istore_2
         5: iload_2
         6: iconst_5
         7: if_icmpge     35
        10: new           #7                  // class java/lang/StringBuilder
        13: dup
        14: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
        17: aload_1
        18: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: iload_2
        22: invokevirtual #10                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        25: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        28: astore_1
        29: iinc          2, 1
        32: goto          5
        35: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
        38: aload_1
        39: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        42: return
      LineNumberTable:
        line 16: 0
        line 17: 3
        line 18: 10
        line 17: 29
        line 20: 35
        line 21: 42
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            5      30     2     i   I
            0      43     0  this   Lcom/gyt/manager/test/Test1;
            3      40     1   str   Ljava/lang/String;
      StackMapTable: number_of_entries = 2
        frame_type = 253 /* append */
          offset_delta = 5
          locals = [ class java/lang/String, int ]
        frame_type = 250 /* chop */
          offset_delta = 29

  public void m2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         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: iconst_5
        12: if_icmpge     27
        15: aload_1
        16: iload_2
        17: invokevirtual #10                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        20: pop
        21: iinc          2, 1
        24: goto          10
        27: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
        30: aload_1
        31: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        34: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        37: return
      LineNumberTable:
        line 24: 0
        line 25: 8
        line 26: 15
        line 25: 21
        line 28: 27
        line 29: 37
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           10      17     2     i   I
            0      38     0  this   Lcom/gyt/manager/test/Test1;
            8      30     1    sb   Ljava/lang/StringBuilder;
      StackMapTable: number_of_entries = 2
        frame_type = 253 /* append */
          offset_delta = 10
          locals = [ class java/lang/StringBuilder, int ]
        frame_type = 250 /* chop */
          offset_delta = 16

一大坨我自己看着都心累,直接看重点:
m1方法中:

10: new           #7                  // class java/lang/StringBuilder
...//此处省略
32: goto          5

m2方法中:

0: new           #7                  // class java/lang/StringBuilder
...//此处省略
24: goto          10

从字节码文件中不难看出,无论是“+操作符拼接”还是“StringBuilder拼接”本质上都是通过new一个StringBuilder对象来达到拼接的目的,结论一:“+操作符拼接”和“StringBuilder拼接”效率一致
关注goto,也就是代码中循环的开始位置,我们不难发现,“+操作符拼接”每循环一次都需要new一个对象,而“StringBuilder拼接”仅仅只new了一次对象,对象的产生和回收都是需要时间的,所以结论二:在执行循环遍历操作时,“StringBuilder拼接”的执行效率远高于“+操作符拼接”的执行效率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值