五种字符串拼接的区别

String +=:将s+=”a”生成字节码,反编译之后,应该是一下代码:

String s=(new StringBuilder()).append(s).append(“a”).toString();

也就是说使用+=的时候是先将String转成了StringBuilder,使用其的append方法进行处理,从内存分配上来讲,又是新给了一个String,然后指向这个新的字符串,与concat相比每次不仅新New出来一块新的内存,并且还new了一个新的StringBuilder对象

 

StringBuffer:StringBuffer对象是可以修改和扩充的,直接在堆里面给分配一块内存,每一次使用append方法的时候,就在这个内存块里面在添加一个a,所以相对String要快一些,但是与StringBuilder相比又要慢一些,这是因为StringBuffer是线程安全的,它的append方法是synchronized声明的(意思是这个方法约么全部执行成功,要么全部不成功),是线程安全的。通俗来讲就是StringBuffe的append有三个步骤,lock,临界区,unclock这三步,由于StringBuffer是多线程的,所以在for循环使用append的时候会有多个线程都分配到项目,在cpu切换内存执行的时候有可能线程1正在临界区添加数据 ,但是还未进入到解锁阶段,这个时候如果cpu切换到另一个线程2执行,线程2就会发现线程1没有解锁,所以线程2就会等待,等待下一次cpu切换到线程1 继续执行,直到线程2发现线程1解锁,那么线程2就会先加锁,以确保自己能够执行完成,StringBuilder的append方法不是synchronized声明的,所以没有加锁和解锁的过程,所以也就没有线程2的等待过程,所以会相对快一些,但是线程是不安全的,容易造成数据丢失

 

StringBuilder: StringBuilder的append方法是直接进入临界区,当cpu切换到当前程序的时候,假如当前cpu第一次执行这个程序,第一次进入append的for循环的时候会讲数据写进去,然后更新一下count,表示已经使用的字符个数,也可以当作下标index来看,然后当下一次cpu切换到线程2的时候,就会往新的index里面放数据,这种情况是基于前面那个线程把数据更行完成并且更新了新的index,但是有的时候当cpu切换到一个新的线程的时候他有可能前面那个线程append的时候,数据填写了,Index没有更新,这个新的线程执行append方法的时候酒还是会在之前的Index的位置去添加数据,这就造成了之前那个线程添加的数据丢失,所以StringBuilder是线程不安全的,但是因为没有等待的过程所以相对较快,从代码上来讲StringBuilder本身有一个字符数组,每一次append就是往这个数组里面添加数据,同理StringBuffer也是一样的

 

concat:是final修饰的类,是不可以被继承以及被修改的,所以每一次s+=”a”的时候事实上是在每一个常量池里面新给一个内存块,每一次的+=都会给一个新的内存,一个新的地址,然后这个变量s指向新的变量,如此十万次就有了十万给内存块被new出来,所以速度相对较慢,从代码上来讲就是每一次先创建一个字符串数组,长度是两个待拼接字符串长度之和,再将两个字符串的值复制到这个字符数组中,并且使用这个数组创建一个新的String对象并且返回,每次都new一个新的String,源代码如下:

public String concat(String str){

int  otherLen=str.length();

if(otherLen ==0){

return this;

}

Int len=value.length;

Char buf[]=Arrays.copyOf(value,len+otherLen);

Str.getChars(buf,len);

Return new String(buf,true);

}

 

StriingUtils.join:与String +=类似,也是使用了StringBuilder来进行操作,但是其中还有很多其他操作,所以耗时比较长,其实StriingUtils.join更加擅长处理字符串数组的拼接

 

所以以上五个方法执行从0个到十万个a的添加,时间上的长短排序如下:

  StringBuilder < StringBuffer < concat < String += < StriingUtils.join

猜你喜欢

转载自blog.csdn.net/weixin_42775190/article/details/88109469