StringBuffer源码解析

String 是我们用到非常多的一个类,对于 String 做大量的操作,如果只使用 String 的话,效率没有那么高。一般会推荐使用 StringBuffer 和 Stringbuilder 来做字符串的操作。
那么 StringBuffer 和 StringBuilder 的区别是什么呢?
StringBuffer 是线程安全的,因为它里面的方法都被 synchronized 关键字修饰,例如 append 方法:

@Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

StringBuilder 是线程不安全的,他里面没有使用 synchronized 关键字修饰

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

其中两个类都是在调用父类的 append 方法,只是 StringBuffer 通过 synchronized 关键字来保证线程安全,当然这样做同时也会降低效率。除非是在单线程环境下,并且非常追求速率的情况下使用 StringBuilder,其他情况下还是推荐使用 StringBuffer 来做字符串的操作。这篇文章也不是对比两个类的优缺点,直接去看这两个类的父类 AbstractStringBuilder 。贴出父类中的 append 方法:

	public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len); // 扩容
        str.getChars(0, len, value, count); // 添加新的 String 到尾部
        count += len; // 长度加上添加进来的长度
        return this;
    }

实际上不管是哪一个,都是在使用父类的方法以及成员变量。

	/** 存储字符的数组 */
    char[] value;

    /** 字符数组中已经使用得到长度 */
    int count;

父类 AbstractStringBuilder 中维护了一个存储字符的数组 value,用来保存字符串的全部字符。count 是用来表示使用了多长的数组。
接下来看一下扩容方法:

	private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }

	void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2; // 扩容为原来的 2 倍加 2
        if (newCapacity - minimumCapacity < 0) // 如果扩容后的长度仍然小于最小扩容长度,则新长度赋值为最小扩容长度
            newCapacity = minimumCapacity;
        if (newCapacity < 0) { // 新长度为负数,超过 int 的最大值,变为了负数
            if (minimumCapacity < 0) // overflow // 最小的扩容长度为负数,也是超过了 int 最大值
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE; // 如果最小扩容长度没有超过 int 最大值,但是原长度翻倍加2后超过了,则把新长度赋值为 int 最大值
        }
        value = Arrays.copyOf(value, newCapacity); // 调用 Arrays.copyOf 生成新长度的数组
    }

扩容方法就是扩容为原来的 2 倍再加 2 ,然后判断新长度的合法性,不合法会抛出 OOM ,合法会复制一个新长度的数组覆盖原来的数组。

真正添加 String 的方法是 getChars

	public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        // 执行到这里,说明没有出现下标越界,调用 arraycopy 把该字符串追加到尾部
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

此时,StringBuffer 的 append 方法才算结束执行。
看完在尾部添加 String 的 append 方法,看一下可以在指定下标处添加的 insert 方法,直接贴出调用的父类的 insert 方法

	public AbstractStringBuilder insert(int offset, String str) {
        if ((offset < 0) || (offset > length()))
            throw new StringIndexOutOfBoundsException(offset);
        if (str == null)
            str = "null";
        int len = str.length();
        ensureCapacityInternal(count + len); // 扩容
        // 从 offset 处开始之后的字符都向后移动 len 长度的位置
        System.arraycopy(value, offset, value, offset + len, count - offset);
        // 把新添加的 String 添加到 offset 位置处
        str.getChars(value, offset);
        count += len;
        return this;
    }

检查下标的合法性后就做扩容操作,然后在将指定位置处以后的元素向后移动新加 String 长度的距离,然后将新 String 添加至指定位置。
删除指定区间的 delete 方法如下,依然用 synchronized 控制,并调用父类方法

	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) {
            // 把 start + len 位置开始的元素向前移动 len 个距离
            System.arraycopy(value, start+len, value, start, count-end);
            count -= len;
        }
        return this;
    }

也是使用了 arraycopy 来操作内部维护的字符数组。

最后看一下字符串反转方法 reverse

	public AbstractStringBuilder reverse() {
        boolean hasSurrogates = false;
        int n = count - 1; // 得到下标最大值
        /* 循环,从中间向两边移动,并交换位置
           count = 长度   n = 下表最大值  j = 下标减一后除
           如果是偶数个->user count  = 4, n = 3, j = 1  交换下标为 1 和 3-1 的两个值,即 s 和 e,然后向两头移动
           如果奇数个->hello count = 5, n = 4, j = 1 交换下标为 1 和 4-1 的两个值,即 e 和第二个 l,然后向两头移动
         */
        for (int j = (n-1) >> 1; j >= 0; j--) {
            int k = n - j;
            char cj = value[j];
            char ck = value[k];
            value[j] = ck;
            value[k] = cj;
            if (Character.isSurrogate(cj) ||
                Character.isSurrogate(ck)) {
                hasSurrogates = true;
            }
        }
        if (hasSurrogates) {
            reverseAllValidSurrogatePairs();
        }
        return this;
    }

看懂循环时下标的指向就很容易理解了,如果光看不好理解的话,可以跟我一样用 debug 看一下值的变化,一步一步的看,就能很容易理解了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值