String、StringBuffer、StringBuilder全面比较

String、StringBuffer、StringBuilder比较

首先,通过查看官方文档,我们可以得到一些关键信息,在下面图中我已经做了标注和翻译。
String

StringBuffer

StringBuilder

然后比较它们的源码:

//String   
public final class String  
{  
        private final char value[];  

         public String(String original) {  
              // 把原字符串original切分成字符数组并赋给value[];  
         }  
}  

//StringBuffer   
public final class StringBuffer extends AbstractStringBuilder  
{  
         char value[]; //继承了父类AbstractStringBuilder中的value[]  
         public StringBuffer(String str) {  
                 super(str.length() + 16); //继承父类的构造器,并创建一个大小为str.length()+16的value[]数组  
                 append(str); //将str切分成字符序列并加入到value[]中  
        }  
}

//StringBuilder   
public final class StringBuilder extends AbstractStringBuilder  
{  
         char value[]; //继承了父类AbstractStringBuilder中的value[]  
         public StringBuilder(String str) {  
                 super(str.length() + 16); //继承父类的构造器,并创建一个大小为str.length()+16的value[]数组  
                 append(str); //将str切分成字符序列并加入到value[]中  
        }  
}

通过比较,发现只有String中的是常量(final)数组,只能被赋值一次。这也验证了那句话“字符串是常量,它们的值在创建之后是不可变的“。

下面结合我们日常的代码,分析一下

String str = "Hello ";
str = str + "World";

第一行代码,创建了一个String对象“Hello “,并被str变量引用。
第二行代码,因为String对象“Hello “是一个常量,是不可变的,所以JVM结合“Hello “和“World“创建了一个新的String对象“Hello World“,并重新被str变量引用。
所以,String对象的值并没有变,变化的只是str的引用。然而,这个过程中我们一共创建了两次String对象。

接下来,我们用StringBuffer来实现

StringBuffer stringBuffer=new StringBuffer("Hello ");
stringBuffer.append("World");

第一行代码,用“Hello “创建了一个StringBuffer对象。
第二行代码,调用stringBuffer的append方法把”World”追加到了”Hello “的尾部,stringBuffer内部的字符序列变了。
所以,整个过程我们只创建了一次StringBuffer对象。

在实际需求中,我们很可能遇到要对某字符串进行大量操作的情况
现在我们把上面的操作重复1000次,记录一下代码执行时间。

String str = "Hello ";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
    str = str + "World";
}
long endTime = System.currentTimeMillis();
Log.d(TAG, "runTime: "+(endTime-startTime));//执行时间100ms左右

用StringBuffer来实现

StringBuffer stringBuffer = new StringBuffer("Hello ");
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
    stringBuffer.append("World");
}
long endTime = System.currentTimeMillis();
Log.d(TAG, "runTime: "+(endTime-startTime));//执行时间0ms-1ms

当我们把1000次增加到10000次,发现用String操作,执行时间在7000ms左右,而用StringBuffer操作,执行时间只有2ms-3ms。在这种情况下,StringBuffer的执行速度远高于String。

下面,我们测试一下另一种情况

String str = "";
String str1 = "Hello ";
String str2 = "World";
str = str1 + str2;//因为字符串串联是通过StringBuilder(或StringBuffer)的append方法实现的,所以这段代码与下方代码等效。

String str = "";
String str1 = "Hello ";
String str2 = "World";
StringBuilder temp=new StringBuilder(str1);//创建了一个StringBuilder对象
temp.append(str2);//调用StringBuilder对象的append方法,得到最终的字符数组
str=temp.toString();//调用StringBuilder对象的toString方法,创建了一个新的String对象,并被str引用。所以,在字符串串联的过程中,jVM又创建了两个对象。
StringBuffer stringBuffer=new StringBuffer("");
String str1 = "Hello ";
String str2 = "World";
stringBuffer.append(str1).append(str2);//串联过程中没有创建新的对象

重复操作1000次

String str = "";
String str1 = "Hello ";
String str2 = "World";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
    str = str + str1 + str2;
}
long endTime = System.currentTimeMillis();
Log.d(TAG, "runTime: "+(endTime-startTime));//执行时间300ms左右

用StringBuffer来实现

StringBuffer stringBuffer = new StringBuffer();
String str1 = "Hello ";
String str2 = "World";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
    stringBuffer.append(str1).append(str2);
}
long endTime = System.currentTimeMillis();
Log.d(TAG, "runTime: "+(endTime-startTime));//执行时间0ms-1ms

重复操作10000次,String耗时10000ms左右,StringBuffer耗时5-10ms

通过这些实验,可以得出结论:在进行大量字符串操作时,我们应该用StringBuffer(或StringBuilder)来代替String,因为StringBuffer(或StringBuilder)的执行速度要比String快得多。

那么问题来了,StringBuffer和StringBuilder有啥区别呢?

通过分析官方文档,StringBuffer可将字符串缓冲区安全地用于多个线程,可以在必要时对这些方法进行同步。与StringBuffer相比,StringBuilder支持所有相同的操作,由于它不执行同步,所以速度更快,但也失去了线程安全。通常应该优先使用 StringBuilder 类。

结合以上信息,最终得出结论:

定义单线程执行速度多线程执行速度多线程安全
String不可变的字符序列最慢最慢安全
StringBuffer线程安全的可变字符序列最快(和StringBuilder相同)稍快安全
StringBuilder非线程安全的可变字符序列最快最快不安全
String:适用于少量的字符串操作的情况

StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

本文主要是参考别人的帖子,加上自己查资料和单元测试验证得出的,如有错误或者遗漏,请直接指出,我会尽量完善。

1、String、StringBuffer与StringBuilder之间区别
2、Java中的String,StringBuilder,StringBuffer三者的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值