String与StringBuilder

今天在突然想为什么在连接字符串时,StringBuffer或是StringBuilder(1.5开始)的速度要好于采用连接符“+”。有没有什么特殊的情况呢?java6对编译器对“+”操作做了优化处理后,是否也遵循上述情况呢?

在java6之前,JAVA是如何处理String间的“+”操作呢?我们知道String保存的不是真正的值,它只是栈空间中一个指向堆空间具体内容的地址,且String是final类型的类,这就意味着每次操作都会产生新的String。比如:

String str = “abc”;//“abc”保存在堆空间,str1在栈空间,保存了“abc”在堆空间的地址
	str +=  “def”;

上述操作,第一步就不需要多说了,对于第二步操作,首先在堆空间中创建新的空间,并做“abc”+“def”的操作,然后将新的堆空间地址指向str。

可见,“+”操作就是一个不断创建新对象的过程并在堆空间中复制原关联的值,这个过程会极大的延缓程序的执行效率,所以在类似的字符串不断累加的操作的,提倡使用StringBuffer和StringBuilder,同时由于StringBuffer是线程安全的,所以效率会略低于StringBuilder。

但是从JAVA6开始,上述情况发生了变化,编译器在遇到“+”操作时,会进行一定程度的优化。我们通过,编译后代码可以看到,编译器根据参与“+”操作的变量类型,而采取了优化处理。

  • 参与类型都为字符串


源代码

编译后代码

String str1 = "100" + "abc" + "zxc";

0: ldc           #2                  // String 100abczxc



  • 参与类型为字符串和基本类型


源代码

编译后代码

String str3 = "100" + 1;

55: ldc           #12                 // String 1001

String str5 = "100" + 'a'

85: ldc           #17                 // String 100a

String str6 = "100" + 100.0;

89: ldc           #18                 // String 100100.0


String str7 = "100" + true;

93: ldc           #19                 // String 100true


String str9 = true + "100";

97: ldc           #20                 // String true100


String str10 = "100" + 1 + 1.0;

101: ldc           #21                 // String 10011.0



  • 参与类型不定


源代码

编译后代码

String str2 = "100" + str1 + 1;

       3: new           #3                  // class java/lang/StringBuilder

       6: dup           

       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V

      10: ldc           #5                  // String 100

      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: iconst_1      

      20: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;

      23: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

String str4 = "100" + Double.valueOf(100);

      59: new           #3                  // class java/lang/StringBuilder

      62: dup           

      63: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V

      66: ldc           #5                  // String 100

      68: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      71: ldc2_w        #13                 // double 100.0d

      74: invokestatic  #15                 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;

      77: invokevirtual #16                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;

      80: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

String str11 = "100" + new String("abc");

      76: new           #3                  // class java/lang/StringBuilder

      79: dup           

      80: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V

      83: ldc           #5                  // String 100

      85: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      88: new           #19                 // class java/lang/String

      91: dup           

      92: ldc           #20                 // String abc

      94: invokespecial #21                 // Method java/lang/String."<init>":(Ljava/lang/String;)V

      97: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

     100: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;



由上可以看到如果参与“+”运算的类型仅为字符串的时候,直接在堆空间内完成操作,不会有额外的操作了;运算类型为字符串和基本类型时,等同于全部为字符串的情况,都是在堆内完成操作;而运算类型增加了其他对象时,编译器就会首先创建一个StringBuilder对象,然后在StringBuilder对象中进行append操作,结束后再将新生成的字符串的堆地址赋值。

所以上述几种情况下,我们没有必要将“+”操作转换为StringBuffer或是StringBuilder操作了,因为编译器会为我们进行转换,转换后效率相差不会太大。但这样程序可以写的更加的简洁、直观、明了。

但是如果涉及到了循环,会是什么情况呢?


源代码

编译后代码

String str8 = "100";

for (int i = 0; i < 100; i++) {

     str8 += i;

 }

     138: ldc           #5                  // String 100

     140: astore        12

     142: iconst_0      

     143: istore        13

     145: iload         13

     147: bipush        100

     149: if_icmpge     180

     152: new           #3                  // class java/lang/StringBuilder

     155: dup           

     156: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V

     159: aload         12

     161: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

     164: iload         13

     166: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;

     169: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

     172: astore        12

     174: iinc          13, 1

     177: goto          145


可以看到每次循环结束后,程序都会重新创建一个新的StringBuilder对象,这样同之前多次创建String对象的情况是一样的,时间浪费在了频繁的创建对象上了,所以在循环的情况下,我们还是采用StringBuilder的好。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值