StringBuffer详细讲解

转载地址:https://blog.****.net/u012877472/article/details/50808554

同时这里有一篇比较好的从底层讲解StringBuffer的文章:http://www.cnblogs.com/dolphin0520/p/3778589.html

1、简介

StringBuffer,由名字可以看出,是一个String的缓冲区,也就是说一个类似于String的字符串缓冲区,和String不同的是,它可以被修改,而且是线程安全的。StringBuffer在任意时刻都有一个特定的字符串序列,不过这个序列和它的长度可以通过一些函数调用进行修改。它的结构层次如下图:

StringBuffer详细讲解

StringBuffer是线程安全的,因此如果有几个线程同时操作StringBuffer,对它来说也只是一个操作序列,所有操作串行发生。

每一个StringBuffer都有一个容量,如果内容的大小不超过容量,StringBuffer就不会分配更大容量的缓冲区;如果需要更大的容量,StringBuffer会自动增加容量。和StringBuffer类似的有StringBuilder,两者之间的操作相同,不过StringBuilder不是线程安全的。虽然如此,由于StringBuilder没有同步,所以它的速度更快一些。

2、StringBuffer的原理

StringBuffer继承了抽象类AbstractStringBuilder,在AbstractStringBuilder类中,有两个字段分别是char[]类型的value和int类型的count,也就是说,StringBuffer本质上是一个字符数组:

[java] view plain copy
  1. /** 
  2.  * The value is used for character storage. 
  3.  */  
  4. char[] value;  
  5.   
  6. /** 
  7.  * The count is the number of characters used. 
  8.  */  
  9. int count;  
value用来存储字符,而count表示数组中已有内容的大小,也就是长度。StringBuffer的主要操作有append、insert等,这些操作都是在value上进行的,而不是像String一样每次操作都要new一个String,因此,StringBuffer在效率上要高于String。有了append、insert等操作,value的大小就会改变,那么StringBuffer是如何操作容量的改变的呢?(初始容量为16)

首先StringBuffer有个继承自AbstractStringBuilder类的ensureCapacity的方法:

[java] view plain copy
  1. @Override  
  2. public synchronized void ensureCapacity(int minimumCapacity) {  
  3.     if (minimumCapacity > value.length) {  
  4.         expandCapacity(minimumCapacity);  
  5.     }  
  6. }  
StringBuffer对其进行了重写,直接调用父类的expandCapacity方法。这个方法用来保证value的长度大于给定的参数minimumCapacity,在父类的expandCapacity方法中这样操作:

[java] view plain copy
  1. /** 
  2.  * This implements the expansion semantics of ensureCapacity with no 
  3.  * size check or synchronization. 
  4.  */  
  5. void expandCapacity(int minimumCapacity) {  
  6.     int newCapacity = value.length * 2 + 2;  
  7.     if (newCapacity - minimumCapacity < 0)  
  8.         newCapacity = minimumCapacity;  
  9.     if (newCapacity < 0) {  
  10.         if (minimumCapacity < 0// overflow  
  11.             throw new OutOfMemoryError();  
  12.         newCapacity = Integer.MAX_VALUE;  
  13.     }  
  14.     value = Arrays.copyOf(value, newCapacity);  
  15. }  
也就是说,value新的大小是max(value.length*2+2,minimumCapacity),上面代码中的第二个判断是为了防止newCapacity溢出。

同时,StringBuffer还有一个直接设置count大小的函数setLength:

[java] view plain copy
  1. @Override  
  2. public synchronized void setLength(int newLength) {  
  3.     toStringCache = null;  
  4.     super.setLength(newLength);  
  5. }  
函数直接调用父类的函数,父类函数如下:

[java] view plain copy
  1. public void setLength(int newLength) {  
  2.     if (newLength < 0)  
  3.         throw new StringIndexOutOfBoundsException(newLength);  
  4.     ensureCapacityInternal(newLength);  
  5.   
  6.     if (count < newLength) {  
  7.         Arrays.fill(value, count, newLength, '\0');  
  8.     }  
  9.   
  10.     count = newLength;  
  11. }  
从代码中可以看出,如果newLength大于count,那么就会在后面添加'\0'补充;如果小于count,就直接使count=newLength。

StringBuffer中的每一个append和insert函数都会调用父类的函数:

[java] view plain copy
  1. @Override  
  2. public synchronized StringBuffer append(Object obj) {  
  3.     toStringCache = null;  
  4.     super.append(String.valueOf(obj));  
  5.     return this;  
  6. }  
  7.   
  8. @Override  
  9. public synchronized StringBuffer insert(int index, char[] str, int offset,  
  10.                                         int len)  
  11. {  
  12.     toStringCache = null;  
  13.     super.insert(index, str, offset, len);  
  14.     return this;  
  15. }  
而在父类中,这些函数都会首先保证value的大小够存储要添加的内容:

[java] view plain copy
  1. public AbstractStringBuilder append(String str) {  
  2.     if (str == null)  
  3.         return appendNull();  
  4.     int len = str.length();  
  5.     ensureCapacityInternal(count + len);  
  6.     str.getChars(0, len, value, count);  
  7.     count += len;  
  8.     return this;  
  9. }  
  10.   
  11. public AbstractStringBuilder insert(int index, char[] str, int offset,  
  12.                                     int len)  
  13. {  
  14.     if ((index < 0) || (index > length()))  
  15.         throw new StringIndexOutOfBoundsException(index);  
  16.     if ((offset < 0) || (len < 0) || (offset > str.length - len))  
  17.         throw new StringIndexOutOfBoundsException(  
  18.             "offset " + offset + ", len " + len + ", str.length "  
  19.             + str.length);  
  20.     ensureCapacityInternal(count + len);  
  21.     System.arraycopy(value, index, value, index + len, count - index);  
  22.     System.arraycopy(str, offset, value, index, len);  
  23.     count += len;  
  24.     return this;  
  25. }  
父类中通过ensureCapacityInternal的函数保证大小,函数如下:

[java] view plain copy
  1. private void ensureCapacityInternal(int minimumCapacity) {  
  2.     // overflow-conscious code  
  3.     if (minimumCapacity - value.length > 0)  
  4.         expandCapacity(minimumCapacity);  
  5. }  
如果空间不够,最终也是调用expandCapacity方法。这样就保证了随着操作value的空间够用。

还有就是,StringBuffer可以通过toString方法转换为String,它有一个私有的字段toStringCache:

[java] view plain copy
  1. /** 
  2.  * A cache of the last value returned by toString. Cleared 
  3.  * whenever the StringBuffer is modified. 
  4.  */  
  5. private transient char[] toStringCache;  
也就是一个缓存,用来保存上一次调用toString的结果,如果value的字符串序列发生改变,就会将它清空。toString代码如下:

[java] view plain copy
  1. @Override  
  2. public synchronized String toString() {  
  3.     if (toStringCache == null) {  
  4.         toStringCache = Arrays.copyOfRange(value, 0, count);  
  5.     }  
  6.     return new String(toStringCache, true);  
  7. }  
首先判断toStringCache是否为null,如果是先将value复制到缓存里,然后使用toStringCache new一个String。

3、常用方法

(1)构造函数

StringBuffer有四个构造函数:

  • StringBuffer() value内容为空,并设置容量为16个字节;
  • StringBuffer(CharSequece seq)  使用seq初始化,容量在此基础上加16;
  • StringBuffer(int capacity) 设置特定容量;
  • StringBuffer(String str)  使用str初始化,容量str大小的基础上加16;

(2)append方法

由于继承了Appendable接口,所以要实现append方法,StringBuffer类对几乎所有的基本类型都重载了append方法:

  • append(boolean b)
  • append(char c)
  • append(char[] str)
  • append(char[] str,int offset,int len)
  • append(CharSequence s)
  • append(CharSequence s,int start,int end)
  • append(double d)
  • append(float f)
  • append(int i)
  • append(long lng)
  • append(Object obj)
  • append(String str)
  • append(StringBuffer sb)

(3)insert方法

insert方法可以控制插入的起始位置,也几乎对所有的基本类型都重载了insert方法:

  • insert(int offser,boolean b)
  • insert(int offset,char c)
  • insert(int offset,char[] str)
  • insert(int index,char[] str,int offset,int len)
  • insert(int dsfOffset,CharSequence s)
  • insert(int dsfOffset,CharSequence s,int start,int end)
  • insert(int offset,double d)
  • insert(int offset,float f)
  • insert(int offset,int i)
  • insert(int offset,long l)
  • insert(int offset,Object obj)
  • insert(int offset,String str)

(4)其它会改变内容的方法

上面的那些方法会增加StringBuffer的内容,还有一些方法可以改变StringBuffer的内容:

  • StringBuffer delete(int start,int end) 删除从start到end(不包含)之间的内容;
  • StringBuffer deleteCharAt(int index) 删除index位置的字符;
  • StringBuffer replace(int start,int end,String str) 用str中的字符替换value中从start到end位置的子序列;
  • StringBuffer reverse() 反转;
  • void setCharAt(int index,char ch) 使用ch替换位置index处的字符;
  • void setLength(int newLength) 可能会改变内容(添加'\0');

(5)其它常用方法

下面这些方法不会改变内容:

  • int capacity() 返回value的大小即容量;
  • int length() 返回内容的大小,即count;
  • char charAt(int index) 返回位置index处的字符;
  • void ensureCapacity(int minimumCapacity) 确保容量至少是minimumCapacity;
  • void getChars(int srcBegin,int srcEnd,char[] dst,int dstBegin) 返回srcBegin到srcEnd的字符到dst;
  • int indexOf(String str) 返回str第一次出现的位置;
  • int indexOf(String str,int fromIndex) 返回从fromIndex开始str第一次出现的位置;
  • int lastIndexOf(String str) 返回str最后出现的位置;
  • int lastIndexOf(String str,int fromIndex) 返回从fromIndex开始最后一次出现str的位置;
  • CharSequence subSequence(int start,int end) 返回字符子序列;
  • String substring(int start) 返回子串;
  • String substring(int start,int end) 返回子串;
  • String toString() 返回value形成的字符串;
  • void trimToSize() 缩小value的容量到真实内容大小;

4、与String和StringBuilder的区别

三者都是处理字符串常用的类,不同在于:

速度上:String<StringBuffer<StringBuilder;

安全上:StringBuffer线程安全,StringBuilder线程不安全;

原因在于,String的每次改变都会涉及到字符数组的复制,而StringBuffer和StringBuilder直接在字符数组上改变;同时,StringBuffer是线程安全的,而StringBuilder线程不安全,没有StringBuffer的同步,所以StringBuilder快于StringBuffer。

总结:

如果对字符串的改变少,使用String;

如果对字符串修改的较多,需要线程安全就用StringBuffer,不需要就使用StringBuilder。