java中的String,StringBuffer和StringBuilder浅谈

开山之作,若讲解不清,思虑不明万望见谅。结论:
(1)String类对象是不可改变的,任何对String对象的改变都会隐形的重新构建一个新的对象,释放原来的String对象
(2)StringBuffer类是可以改变的,任何对StringBuffer对象的改变都不会产生新的对象,StringBuffer对象通过append()方法来修改值
(3)StringBuilder类和StringBuffer类几乎没有任何的差别,只是StringBuilder不支持线程同步不具有线程安全。这就使得在单线程中StringBuilder的效率要高于StringBuffer。


1.为什么在for循环中要慎用String拼接?

效率问题,要清楚的理解这个问题还要从字符串拼接的原理说起。有如下例子:

public class Test
{
        public static void main(String args[])
        {
                String a="a";
                String b="b";
                String c="c";
                String d=a+b+c;
        }
}
再上面的例子中实现了两个字符串的拼接,编译之后通过javap -verbose得到如下信息(截取了主要片段):

<span style="font-size:12px;"> Code:
      stack=2, locals=5, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String c
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: aload_3
        25: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        28: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        31: astore        4
        33: return
</span>
从第6行到第21行,我们看到String d=a+b+c;被编译为:

<span style="font-size:12px;">String c=new StringBuilder().append(a).append(b).append(c).toString()</span>
通过新构造一个StringBuilder对象,调用append方法若干次,再toString得到一个新字符串实现的字符串的拼接。


了解了字符串的拼接之后我们再去看看for循环中是怎么样的呢?有如下代码:

<span style="font-size:12px;">public class Test
{
	public static void main(String args[])
	{
		String a="a";
		String b="b";
		for(int i=0;i<4;i++)
		{
			a=a+b;
		}
	}
}</span>
这里实现了一个循环拼接字符串的例子,javap -vrebose得到如下信息(部分截取):

<span style="font-size:12px;"> Code:
      stack=2, locals=4, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: iconst_0
         7: istore_3
         8: iload_3
         9: iconst_4
        10: if_icmpge     38
        13: new           #4                  // class java/lang/StringBuilder
        16: dup
        17: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
        20: aload_1
        21: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: aload_2
        25: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        28: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        31: astore_1
        32: iinc          3, 1
        35: goto          8
        38: return
</span>

从第8行到第35行是循环体,第35行的goto是跳转的意思,在循环体里,每次循环都会创建一个StringBuilder从而获得本次拼接的字符串,我们都知道创建对象是需要开销的。在这里循环的越多,开销也就越大。


2.String,StringBuilder和StringBuffer性能比较

我们通过for循环来比较三者的性能,有如下代码:

<span style="font-size:12px;">public class Test
{
        public static void main(String args[])
        {
                String a="a";	
                StringBuilder b=new StringBuilder("b");
                StringBuffer c=new StringBuffer("c");
                
                long start_time=0L;
                long end_time=0L;
                //字符串拼接用时
                start_time=System.currentTimeMillis();
                for(int i=0;i<100000;i++)
                {
                	a=a+"a";
                }
                end_time=System.currentTimeMillis();
                System.out.println("String use time="+(end_time-start_time));
                //StringBuilder用时
                start_time=System.currentTimeMillis();
                for(int i=0;i<100000;i++)
                {
                	b.append("b");
                }	
                end_time=System.currentTimeMillis();
                System.out.println("StringBuilder use time="+(end_time-start_time));
                //StringBuffer用时
                start_time=System.currentTimeMillis();
                for(int i=0;i<100000;i++)
                {
                	c.append("c");
                }	
                end_time=System.currentTimeMillis();
                System.out.println("StringBuffer use time="+(end_time-start_time));
        }
}
</span>
在我的电脑上执行后的结果为:

<span style="font-size:12px;">String use time=11714
StringBuilder use time=4
StringBuffer use time=5</span>
可见对于同等数量的循环中string的拼接是非常低效的,由于不支持多线程所以StringBuilder的效率比StringBuffer要高。


总结:

在单线程中,若非循环拼接字符串,使用String,StringBuilder和StringBuffer都是可以的,这种情况下String拼接更为简单易懂。StringBuffer略显大材小用。

在单线程循环拼接字符串时,使用StringBuilder最为合理。

在多线程中StringBuffer是不二人选!





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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值