StringBuilder和StringBuffer的扩容问题

2022-07-11 09:06:28

1.继承体系

StringBuilder类:

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

StringBuffer类

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

         StringBuilder 和StringBuffer都继承自AbstractStringBuilder这个抽象类

2.构造方法

        StringBuffer类(共四个)

     public StringBuffer() {
        super(16);
    }

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

   public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }

   public StringBuffer(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

        StringBuilder类的构造方法和StringBuffer的构造方法个数,参数和方法体完全一样

父类构造方法:共两个

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

     AbstractStringBuilder() {
    }

        父类关键成员:

  char[] value; //底层用字符数组实现
 
  int count;   //记录当前数组中存放的有效数据长度

        测试:

public static void main(String[] args) {
        StringBuilder sb=new StringBuilder();
        StringBuilder sb2=new StringBuilder("hello");
        StringBuilder sb3=new StringBuilder(2);

        System.out.println(sb.length()); // 0
        System.out.println(sb.capacity()); // 16

        System.out.println(sb2.length()); // 5
        System.out.println(sb2.capacity()); // 5+16=21

        System.out.println(sb3.length()); // 0
        System.out.println(sb3.capacity());  // 2
    }

从源码可知:使用StringBuilder无参构造方法创建一个对象,会使用父类的有参构造方法,默认开辟一个长度为16的字符数组。

如果使用字符串的有参构造,则会在当前字符串长度的基础上+16

也可以使用另一个有参构造直接指定底层数组的长度

length和capacity方法都是使用的父类中的方法:

  public int length() {
        return count;
    }

  public int capacity() {
        return value.length;
    }

3.常用方法:append(String s): 作用是拼接字符串并返回当前StringBuilder对象

    
   public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

    //父类中的append方法
   public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len); //拼接后的字符串长度作为参数,里面会调用复制数组的方法。
        str.getChars(0, len, value, count);  //复制str到新数组的后半部分
        count += len;
        return this;
    }

    //判断是否需要扩容,不需要直接退出
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) { // 满足说明需要扩容
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }      
         /*
            copyOf方法的作用是将value数组复制到指定长度也就是newCapacity(minimumCapacity)的返回     
           值的新数组中     
         */
    }
    
    //该方法的作用是返回数组需要扩容到的长度
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2; //相当于value.length*2+2
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;  //如果扩容后还是小于需要的最小长度,则newCapacity直接等于最小的长度。
        }
        
           // 上面如果value.length值很大,则newCapacity有可能越界,越界则为负数或0

            //如果越界或者newCapacity大于了MAX_ARRAY_SIZE,则返回hugeCapacity(minCapacity)
            //否则返回newCapacity
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }
    
     private int hugeCapacity(int minCapacity) {
        if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }

总结:append(String s) 方法,会首先判断是否需要扩容,不需要则直接将参数字符串复制到源字符数组后面,如果需要扩容,则使用类的扩容机制也就是 newCapacity()方法返回需要扩容到的容量,ensureCapacityInternal方法会将源数组复制到扩容好的新数组中,然后在调用字符串的getChars方法,将需要拼接的字符串复制到新数组的后面,完成拼接,返回对象本身。

StringBuffer和StringBuilder一样,都是调用的同一个父类中的append方法,所以扩容的方式相同。

append方法有很多重载,几乎可以拼接所有其他类型,StringBuffer和StringBuilder可以相互拼接。

4.StringBuffer和StringBuilder的不同点

      4.1 StringBuilder中几乎所有的public方法都是直接调用的父类中对应的方法来实现的,而StringBuffer因为被设计成线程安全的类,所以大部分方法都是重写了父类中的对应方法,并且加上关键字synchronized,但是方法体的实现还是调用父类中的方法。

如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。

      4.2 toString实现不同

//StringBuffer中:
public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }
//StringBuilder中:
  public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }
  • 作者:weixin_52907605
  • 原文链接:https://blog.csdn.net/weixin_52907605/article/details/121194630
    更新时间:2022-07-11 09:06:28