StringBuider源码解析

一、StringBuider和StringBuffer

大家都知道String是不可变类,在内存中有个字符串常量池的地方,顾名思义,这块内存保存着各种字符串,它们是不重复的,一个字符串变量引用着这块内存的某个字符串,因此,当出现不同的字符串时,并不会修改字符串池中已有的字符串,而是新增。
StringBuider和StringBuffer就可以改变字符串而不是不断地新建,这两个的主要区别是StringBuffer使用了synchronized来进行线程同步,因此是线程安全的,适用于多线程。相反,StringBuider更适用于单线程,效率会更高。

二、StringBuider解析

因为StringBuider和StringBuffer的主要区别就是StringBuffer在操作时代码块中多了synchronized关键字,其他和StringBuider差不多,因此,以StringBuider为例,解析下它们的实现原理。

不知道大家在使用时有没有注意到StringBuider的length和capacity返回结果往往不同,查看源码发现,StringBuider有个关键父类AbstractStringBuilder,这个类定义了length和capacity方法:

public int length() {
        return count;
    }

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

先大概说下,value其实就是个char数组,capacity返回的就是这个数组总容量,因为数组不一定是满的,count则是数组里面实际字符数。

下面从它的构造函数开始,讲下有代表性的三种构造函数:

public StringBuilder() {
        super(16);
    }

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

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

观察可以发现,第一种是无参的,第二种是自定义大小的,第三种是使用一个字符串来创建。
这三个构造函数都会先调用super(int capacity),看下在AbstractStringBuilder中的实现:

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

很简单,前面说过value是一个char数组,在构造函数中则使用传进来的参数新建value数组。再看下这个参数capacity的来源,也就是上面说的那三种构造函数,第一种是无参的,他会使用16来作为默认数组容量,第二种是自定义容量,第三种则是str.length() + 16,也就是字符串的大小加上16作为value数组的初始容量。

说到这里,我们可以知道,在初始化时,会根据我们的需要新建一个char数组,之后的操作其实就是操作这个数组。

再来看下扩容问题,其实数组也是个对象,一旦创建,它的最大容量就确定了,超过会发出越界异常,因此想扩容的话,只能再新建更大的数组。
下面分析下append方法,看下是怎么实现扩容的:

public AbstractStringBuilder append(String str) {
        if (str == null) str = "null";
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getCharsNoCheck(0, len, value, count);
        count += len;
        return this;
    }

新添加字符串str的长度是len,还记得count是表示当前数组中实际字符数,继续调用ensureCapacityInternal(count + len):

void ensureCapacityInternal(int minimumCapacity) {

        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }

minimumCapacity就是count+len,先做个判断,这个判断的意思就是在添加字符串后,总的长度是否超过当前数组的最大容量,如果超过了,继续调用expandCapacity(minimumCapacity):

void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
    }

注意第一句话,newCapacity新的容量等于当前容量的两倍再加2,然后进行一个判断,如果newCapacity仍然小于minimumCapacity(前面说的count+len),则newCapacity直接等于minimumCapacity。这句活的意思就是,第一次扩容会直接扩大两倍加2,在之后还需要扩容的话,容量会直接等于实际长度。举个例子,开始容量是16,如果数组实际字符数小于16,调用capacity方法,会一直返回16;当append后如果实际长度达到了17或更大,这时新的容量会变成16*2+2=34,调用capacity方法,会返回34;当继续append,实际长度又超过34后,这时新的容量会等于实际长度,也就是capacity方法和length方法会得到相同结果。继续看最后一行代码,这就是使用newCapacity新建一个数组。

StringBuider的重要知识点就先说到这了,其他一些常用方法其实就是操作数组,需要注意的就是扩容的问题,以及在各个阶段capacity和length方法的返回值区别。欢迎大家交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值