StringBuffer详解

StringBuffer详解

概述#

​ StringBuffer:字符串变量(Synchronized,即线程安全)。如果要频繁对字符串内容进行修改,出于效率考虑最好使用 StringBuffer,如果想转成 String 类型,可以调用 StringBuffer 的 toString() 方法。

​ Java.lang.StringBuffer 线程安全的可变字符序列。在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。可将字符串缓冲区安全地用于多个线程。

​ StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。

  • append 方法始终将这些字符添加到缓冲区的末端;
  • insert 方法则在指定的点添加字符。
    例如,如果 z 引用一个当前内容是 start 的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含 startle ,而 z.insert(4, “le”) 将更改字符串缓冲区,使之包含 starlet 。

StringBufferApi#

1.添加
StringBuffer append():将指定数据作为参数添加到已有数据结尾处。

StringBuffer insert(index,数据):将指定数据作为参数添加到指定的index位置。

2.删除

StringBuffer delete(start,end):删除缓冲区中的数据,包含start,不包含end。

StringBuffer deleteCharAt(index):删除指定位置的字符。

3.修改

StringBuffer replace(start,end,string):将指定的位置范围替换成指定的数据。

void setCharAt(int index,char ch):将指定的位置替换成指定的数据。

4.反转

StringBuffer reverse():将容器里的数据反向输出。

源码解读#

1:定义#

public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence

2:属性#

 static final long serialVersionUID = 3388685877147921107L;

3:构造函数#

(1)构造一个其中不带字符的字符串缓冲区,初始容量为 16 个字符。

 public StringBuffer() {
        super(16);
    }

继承父类的AbstractStringBuilder的构造函数

AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

(2)构造一个不带字符,但具有指定初始容量的字符串缓冲区

public StringBuffer(int capacity) {
       super(capacity);
    }

(3)构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。该字符串的初始容量为 16 加上字符串参数的长度

public StringBuffer(String str) {
//这个执行父类的带参构造函数AbstractStringBuilder(int capacity) 
        super(str.length() + 16);
       append(str);
    }
append 是用 synchronized 修饰的,所以是线程安全的
public synchronized StringBuffer append(String str) {  
       //执行父类的append(str)  
        super.append(str);  
       return this;  
} 

父类 AbstractStringBuilder 的 append 方法

 public AbstractStringBuilder append(String str) {
       if (str == null) str = "null";
       int len = str.length();
       ensureCapacityInternal(count + len);//扩大容量      
       str.getChars(0, len, value, count);
       count += len;
       return this;
   }

//扩大容量

private void ensureCapacityInternal(int minimumCapacity) {  



        // overflow-conscious code:判断是否需要扩容,也就是说原来的capacity是否足够大  



        if (minimumCapacity - value.length > 0) //20-19=1,1>0  



            expandCapacity(minimumCapacity);  



    }  
void expandCapacity(int minimumCapacity) {



        //新容量  原始容量 * 2 + 2



        int newCapacity = value.length * 2 + 2;



        if (newCapacity - minimumCapacity < 0)     //扩容后的容量-字符串实际长度<0(就是说如果扩容后还装不下),



            newCapacity = minimumCapacity;    //则使用字符串实际长度作为StringBuffer的capacity 



        if (newCapacity < 0) {//扩容后的容量超过integer的最大值



           if (minimumCapacity < 0) // overflow //最终容量超过integer的最大值



               throw new OutOfMemoryError();



            newCapacity = Integer.MAX_VALUE;



        }



        //将旧的值剪切到新的字符数组。



        value = Arrays.copyOf(value, newCapacity);



    }

4:常用方法#

4.1 append 源码#

(1)参数为StringBuffer sb
public AbstractStringBuilder append(StringBuffer sb) {  
 if (sb == null)  
        return append("null");  
   int len = sb.length();  
   int newCount = count + len;    
  if (newCount > value.length)  
       expandCapacity(newCount); // 若value存储容量不够需扩容。扩容方法省略暂不分析,基本上根据Arrays.copyOf()方法,复制指定的数组,以使副本具有指定的长度。到头来copyOf的源码一样是利用arraycopy方法来复制数组和扩容  
   sb.getChars(0, len, value, count); //把sb的内容0->len复制到value中,注意value参数,此时的value是已经扩容后的value。
  count = newCount;  // 更新新count值  
   return this;  
}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) //  dstBegin就是原有count接下来的位置  
   {  
   if (srcBegin < 0)  
      throw new StringIndexOutOfBoundsException(srcBegin);  
 if ((srcEnd < 0) || (srcEnd > count))  
     throw new StringIndexOutOfBoundsException(srcEnd);  
     if (srcBegin > srcEnd)  
        throw new StringIndexOutOfBoundsException("srcBegin > srcEnd"); //以上是Bound Check  
  System.arraycopy(value, srcBegin,dst, dstBegin, srcEnd - srcBegin);  // 从指定源数组中复制一个数组  
}   //AbstractStringBuilder里定义的getChars方法体,是对System.arraycopy方法 + 边界检查的进一步封装而已。
(2)参数为char[] str
public AbstractStringBuilder append(char[] str) {
       int len = str.length;    //数组长度
       ensureCapacityInternal(count + len);    //扩容
     System.arraycopy(str, 0, value, count, len);
      count += len;
     return this;
   }
(3) 添加字符数组str从偏移量offset开始,长度为len的子数组。
public AbstractStringBuilder append(char str[], int offset, int len) {
       if (len > 0)                
      ensureCapacityInternal(count + len);
    System.arraycopy(str, offset, value, count, len);
    count += len;
     return this;
   }
(4)参数为boolean b
public AbstractStringBuilder append(boolean b) {
      if (b) {
        ensureCapacityInternal(count + 4);
       value[count++] = 't';
        value[count++] = 'r';
      value[count++] = 'u';
       value[count++] = 'e';
   } else {
       ensureCapacityInternal(count + 5);
      value[count++] = 'f';
      value[count++] = 'a';
      value[count++] = 'l';
        value[count++] = 's';
       value[count++] = 'e';
    }
     return this;
  }

(5)char c

public AbstractStringBuilder append(char c) {
    //字符的长度为 1 ,只需增加一个
     ensureCapacityInternal(count + 1);
     value[count++] = c;
     return this;
  }

(6)int i

public AbstractStringBuilder append(int i) {  
  if (i == Integer.MIN_VALUE) {  
      append("-2147483648");  
      return this;  
    }  
   // stringSizeOfInt,判断输入的整数i占多少位,如负数+1,方法源码往后看。  
    int appendedLength = (i < 0) ? stringSizeOfInt(-i) + 1 : stringSizeOfInt(i);  
   int spaceNeeded = count + appendedLength;  
   if (spaceNeeded > value.length) // 检查容量,是否需扩容  
       expandCapacity(spaceNeeded);  
Integer.getChars(i, spaceNeeded, value); // 把整型i以字符的形式加进buffer(value)中  
 count = spaceNeeded; //更新count长度。  
   return this;  
 }
//占位表  
 final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,99999999, 999999999, Integer.MAX_VALUE };  
 // Requires positive x  
   static int stringSizeOfInt(int x) {  
     for (int i=0; ; i++)  
          if (x <= sizeTable[i]) // 无限循环将整型x与占位表逐一对比,例如x=100,将与占位表索引为2的999匹配。  
              return i+1; //返回2+1,占3位。  
   } 

(7) 参数为long l

    public AbstractStringBuilder append(long l) {
    if (l == Long.MIN_VALUE) {
          append("-9223372036854775808");
          return this;
      }
      int appendedLength = (l < 0) ? Long.stringSize(-l) + 1: Long.stringSize(l);
      int spaceNeeded = count + appendedLength;
      ensureCapacityInternal(spaceNeeded);
       Long.getChars(l, spaceNeeded, value);

     count = spaceNeeded;
     return this;  
    }
		//这个是计算long的长度的方法,利用Long.getChars将内容复制入新字符数组中
      static int stringSizeOfLong(long x) {  
           long p = 10;  
         for (int i=1; i<19; i++) {  
              if (x < p) // 与两位数10比较,小于返回占一位  
                return i;  
              p = 10*p; //如此类推,小于100占2位  
       }  
        return 19; // 最大19位  
     }  

4.2 delete#

delete(int start, int end) 删除字符序列中从start开始,end结束的子序列

public AbstractStringBuilder delete(int start, int end) {
     if (start < 0)
          throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            end = count;
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        int len = end - start;
        if (len > 0) {
            System.arraycopy(value, start+len, value, start, count-end);
            count -= len;
      }
       return this;
    }

deleteCharAt(int index) 直接删除一个字符,相当于delete(index,index+1)

    public AbstractStringBuilder deleteCharAt(int index) {
     if ((index < 0) || (index >= count))
         throw new StringIndexOutOfBoundsException(index);
     System.arraycopy(value, index+1, value, index, count-index-1);
       count--;
       return this;
   }

4.3 replace#

public AbstractStringBuilder replace(int start, int end, String str) {  
      if (start < 0)  
throw new StringIndexOutOfBoundsException(start);  
  if (start > count)  
       throw new StringIndexOutOfBoundsException("start > length()");  
   if (start > end)  
      throw new StringIndexOutOfBoundsException("start > end");  
      // 以上都是一些Bounds Checking,没什么好说。  
   if (end > count) //若替换字符边界超出字符长度,把字符长度赋予边界量  
       end = count;  
   int len = str.length();  
    //新字符长度,要加上替换长度,减去截取长度  
  int newCount = count + len - (end - start);  
  if (newCount > value.length) //是否大于已有容量,是则扩容  
    expandCapacity(newCount);  
    //复制数组,先预留替换字符串str的长度,看后面的解释。  
   System.arraycopy(value, end, value, start + len, count - end);  
    str.getChars(value, start);//填充str到value,start索引开始  
    count = newCount;  
    return this;  
  }

4.4 insert 方法#

其实跟replace差不多,都是先通过System.arraycopy Method在字符数组value中预留一个空间,再用多一次这方法插入str或char[]。

(1)insert(int index, char str[], int offset,int len)

// StringBuffer里面,调用父类的方法  
public synchronized StringBuffer insert(int index, char str[], int offset, int len)   
{ 		super.insert(index, str, offset, len);  
 			return this;  
}
 public AbstractStringBuilder insert(int index, char str[], int offset,int len)  
{				// bounds checking  
			if ((index < 0) || (index > length()))  
				throw new StringIndexOutOfBoundsException(index);  
			// 注意:offset的值,offset + len不应大于str.length  
			if ((offset < 0) || (len < 0) || (offset > str.length - len))   
					 throw new StringIndexOutOfBoundsException("offset " + offset + ", len " + len + ", str.length " + str.length);  
			int newCount = count + len;  
			if (newCount > value.length)  
				expandCapacity(newCount);  
			// 从index开始,预留了长度为len的空间;从index+len开始,将长度为count-index的后半截补充。  
			System.arraycopy(value, index, value, index + len, count - index);  
            // str字符串数组,从索引offset开始复制,长度为len,复制到以索引index位置的value字符串数组。  
            System.arraycopy(str, offset, value, index, len);  
            count = newCount; //记得更新实际字符长度(字符数量)。  
            return this;  
} 
posted @   卷皇  阅读(218)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
点击右上角即可分享
微信分享提示
主题色彩