定义:
从jdk1.5开始提供的新的封装字符串的类,StringBuilder,其字符串拼接操作的效率远远高于 String。
Java里面提供了String,StringBuffer和StringBuilder三个类来封装字符串
简介:
我们知道字符串其实就是由若干个字符线性排列而成的,可以理解为字符数组Array,那么既然是数组实现的,那就需要考虑到数组的特性,数组在内存中是一块连续的地址空间块,即在定义数组的时候需要指定数组的大小
换言之, 数组就分为可变数组和不可变数组。可变数组能够动态插入和删除,而不可变数组一旦分配好空间后则不能进行动态插入或删除操作。
在实际的字符串应用场景中,涉及到多种操作,比如字符串的插入,删除,修改,拼接,查询,替换...
String:
不可变类,属性value为不可变数组,即String初始化构造器没有初始容量为16的概念,你定义多少,String中字符数组的长度就是多少,不存在字符数组扩容一说。看下源码:
final修饰的String 类,以及final修饰的char[] value,表示String类不可被继承,且value只能被初始化一次。这里的value变量其实就是存储了String字符串中的所有字符。
那既然String,不可变。我们再看下它的截取方法subString()实现
这里可以看到,在substring方法中,如果传入的参数为0,就返回自身原对象,否则就是重新创建一个新的对象。
类似的我们可以看到,String类的concat方法,replace方法,都是内部重新生成一个String对象的。
这也就是为什么我们如果采用String对象频繁的进行拼接,截取,替换操作效率很低下的原因。
下面再看下StringBuilder对象的源码,分析为何其在做字符串的拼接,截取,替换方面效率远远高于String
StringBuilder:
内部可变数组,存在初始化StringBuilder对象中字符数组容量为16,存在扩容。
StringBuilder类继承AbstractStringBuilder抽象类,其中StringBuilder的大部分方法都是直接调用的父类的实现。
首先看下StringBuilder的构造方法
1:空参数的构造方法
2:自定义初始容量-构造函数
3:以字符串String 作为参数的构造
在参数Str 数组长度的基础上再增加16个字符长度,作为StringBuilder实例的初始数组容量,并将str字符串 append到StringBuilder的数组中。
具体看下父类AbstractStringBuilder的append方法
1:首先判断append的参数是否为null,如果为null的话,这里也是可以append进去的
其中ensureCapacityInternal方法是确保这次append 的时候StringBuilder的内部数组容量是满足的,即这次要append的null字符长度为4,加上之前内部数组中已有的字符位数c之后作为参数执行。
2:如果不为null的话,就获取这次需要append的str的字符长度。紧接着执行是否需要扩容的方法
3:重点看下append方法的关键:String的 getChars方法(从str的0位开始,到str的长度,当前StringBuilder对象的字符数组,当前数组已有的字符长度)
其实是调用了System的arraycopy方法 参数如下:
value 为str的内部不可变字符数组,
srcBegin 为从str 字符串数组的0下标开始,
srcEnd 为str字符串数组的长度,
dst 为StringBuilder对象的内部可变字符数组,
dstBegin 则为StringBuilder对象中已有的字符长度(char[] 已有的元素长度)
即整个StringBuilder的append方法,本质上是调用System的native方法,直接将String 类型的str字符串中的字符数组,拷贝到了StringBuilder的字符数组中
toString():
最后说下StringBuilder的toString方法,
这里的toString方法直接new 一个String对象,将StringBuilder对象的value进行一个拷贝,重新生成一个对象,不共享之前StringBuilder的char[]
以上就是StringBuilder的拼接字符串的原理分析,可以发现没有像String一样去重新new 对象,所以在频繁的拼接字符上,StringBuilder的效率远远高于String类。
StringBuffer:
线程安全的高效字符串操作类,看下源码:
类图和StringBuilder一样,不多说
和StringBuilder一样,也不用多说,重点看下其append方法:
可以看到这里就是在append方法上加了同步锁,来实现多线程下的线程安全。其他的和StringBuilder一致。
这里比StringBuilder多了一个参数
这里的作用简单介绍一下,就是去缓存toString的
可以看下StringBuffer的toString方法
这里的作用就是如果StringBuffer对象此时存在toStringCache,在多次调用其toString方法时,其new出来的String对象是会共享同一个char[] 内存的,达到共享的目的。但是StringBuffer只要做了修改,其toStringCache属性值都会置null处理。这也是StringBuffer和StringBuilder的一个区别点。
总结:
String 类不可变,内部维护的char[] 数组长度不可变,为final修饰,String类也是final修饰,不存在扩容。字符串拼接,截取,都会生成一个新的对象。频繁操作字符串效率低下,因为每次都会生成新的对象。
StringBuilder 类内部维护可变长度char[] , 初始化数组容量为16,存在扩容, 其append拼接字符串方法内部调用System的native方法,进行数组的拷贝,不会重新生成新的StringBuilder对象。非线程安全的字符串操作类, 其每次调用 toString方法而重新生成的String对象,不会共享StringBuilder对象内部的char[],会进行一次char[]的copy操作。
StringBuffer 类内部维护可变长度char[], 基本上与StringBuilder一致,但其为线程安全的字符串操作类,大部分方法都采用了Synchronized关键字修改,以此来实现在多线程下的操作字符串的安全性。其toString方法而重新生成的String对象,会共享StringBuffer对象中的toStringCache属性(char[]),但是每次的StringBuffer对象修改,都会置null该属性值。