StringBuilder底层原理

StringBuilder底层是一个char[ ] ,何以见得,上源码:

public final class StringBuilder
    extends AbstractStringBuilder    //继承了一个类
    implements java.io.Serializable, CharSequence    //实现了两个接口
{
    ……
    ……        //类的具体内容暂时省略掉
}

在继承的AbstractStringBuilder类中        可以很清楚的看到是一个char[ ] 数组,数组名是value(记住数组名哈,后面有用)

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;        //可以很清楚的看到是一个char[] 数组,数组名是value

    /**
     * The count is the number of characters used.
     */
    int count;

    …………        //类的具体内容先省略掉
    
}

下面来详细说明一下几种不同情况添加元素的方式:

1、直接用无参构造器添加

添加时需要搭配append()方法

先上无参构造的源码:

public StringBuilder() {
        super(16);    //调用了父类的无参构造方法
    }

父类的无参构造方法       在父类的无参构造方法中,将16传进来,new了一个长度为16的数组

AbstractStringBuilder(int capacity) {
        value = new char[capacity];    //将数组长度设置为16
    }

以上可以见得,通过无参构造进行添加元素,数组的初始长度为16。

通过调用append()添加元素时:

@Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

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

一种是添加任意类型的元素,后将其转为字符串。一种为直接添加字符串。两种方式最终的结果是一样的。

 public AbstractStringBuilder append(String str) {
        if (str == null)    //当添加的字符串为空时
            return appendNull();    //进入该方法
        int len = str.length();    //不为空时,用len变量来承接字符串的长度
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

由上面源代码可分为两种情况。

(1)当添加的字符串为null时。

private AbstractStringBuilder appendNull() {
        int c = count;
        ensureCapacityInternal(c + 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }

可以在源码上看到当str==null时,是将null转换为字符串添加进入value[ ]中。此时数组中的内容为"null"四个字符。

(2)当添加的字符串不为null时

private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

将添加的字符串的长度+ 原数组元素的个数(此时为第一次添加,原数组元素个数为0,以下将字符串长度即说明为两者之和)和value[ ]的长度作比较,

①如果添加的字符串长度大于当前数组的长度(为初始化长度16)

 private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }

进行数组长度扩容,如何扩容?        value.length << 1 + 2

由以上代码可以看到,先将  数组长度左移1位,再+2。意思就是将初始的数组长度✖2 +2(即位34)。扩容后再与要添加的字符串长度相比较。

if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
 }

如果新扩容的数组长度仍小于要添加的字符串的长度。则直接将要添加的字符串长度直接当作数组新长度。

最后对这个新长度做个判断,如果该长度小于0或者该长度大于最大限制长度。抛出异常(此源代码未展示)。否则就返回该新长度。

执行:value = Arrays.copyOf(value,newCapacity(minimumCapacity));

copyOf()方法

public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

即将添加的字符串逐个添加进数组元素中,并返回copyOf[ ] ,就完成了无参构造的添加元素操作啦。

②如果添加的字符串长度小于初始化数组的长度。(即小于16)。直接要添加的字符串添加入数组中。

2、通过含参构造器添加字符时。

    public StringBuilder(int capacity) {        //直接添加一个长度
        super(capacity);
    }

    
    public StringBuilder(String str) {        //添加一个字符串
        super(str.length() + 16);
        append(str);
    }

    
    public StringBuilder(CharSequence seq) {    //采用多态形式添加
        this(seq.length() + 16);
        append(seq);
    }

①直接添加一个长度

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

直接将该长度作为数组长度。

 

②添加一个字符串。        调用父类的含参构造。创建一个长度为字符串长度+16的一个数组

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

说明:通过含参构造添加字符串,数组的默认长度是:字符串的长度+16。

public StringBuilder(String str) {        //添加一个字符串
        super(str.length() + 16);
        append(str);
    }

此时要注意当添加的字符串为null时(即String str == null)。str.length()会直接报错。在此处和append()添加不一样。

然后执行添加操作append(str)。

③CharSequence seq        为一个接口。该方式可通过该接口的实现类进行传参。(多态性)

感兴趣的朋友们可以自己看一下该接口的源码。基本上和上述的原理相同。在此不过多论述。

总结:StringBuilder的底层是一个char [ ]。

通过无参构造,append()添加元素时。数组的默认长度为16,每次扩容为数组原长度的2倍+2(value.length<<1+2)。

通过含参构造添加元素时。

①直接添加一个长度。数组的初始长度为该长度

②添加一个字符串。数组的初始长度为该字符串的长度+16(str.length+16)。每次扩容为数组原长度的2倍+2(和上述append()方法相同,因为也是通过append()添加元素的)

看完留给一个代码小测验。看看自己的理解程度。

public static void main(String[] args) {
    String str = null;    
    StringBuilder str1  = new StringBuilder();
    str1.append(str);
    System.out.println(str1.length());

    StringBuilder str2 = new StringBuilder(str);
    Sustem.out.println(str2);
}

结果为 :

4

java.lang.NullPointerException(报错)

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值