【Java】JDK源码分析——AbstractStringBuilder

一.概述

AbstractStringBuilder是一个抽象类,StringBuilder和StringBuffer继承该类。
AbstractStringBuilder.java中的相关代码:

	abstract class AbstractStringBuilder implements Appendable, CharSequence {}

1.该类实现了Appendable接口,说明该类的对象可以附加其他对象。
2. 该类实现了CharSequence接口,说明该类的对象可以当作字符处理

二.源码分析

1.重要的全局变量

AbstractStringBuilder.java中的相关代码:

    char[] value; // 用于保存字符串

	int count; // 用于记录value中已经使用的空间的数量,即字符串长度

	// 该常量为数组扩容的最大容量
	// 减8是因为,一些虚拟机在数组对象中需要存储对象头
	// 不减8,可能会内存溢出
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 

2.构造方法

AbstractStringBuilder.java中的相关代码:

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

指定初始化容量,根据容量对数组进行初始化。

3. length方法

获取字符串长度。
AbstractStringBuilder.java中的相关代码:

    @Override
    public int length() {
        return count;
	}

4. capacity方法

获取容量,即最多可以保存的字符数量。
AbstractStringBuilder.java中的相关代码:

	public int capacity() {
    	// 返回数组长度
        return value.length;
	}

5. ensureCapacity方法

对数组的容量进行检测,并在容量小于要求时进行扩容。
AbstractStringBuilder.java中的相关代码:

	public void ensureCapacity(int minimumCapacity) {
    	// 若要求的容量大于0
        if (minimumCapacity > 0)
            // 则调用ensureCapacityInternal方法
            ensureCapacityInternal(minimumCapacity);
	}

1)ensureCapacityInternal方法

AbstractStringBuilder.java中的相关代码:

    private void ensureCapacityInternal(int minimumCapacity) {
        // 若要求的容量大于当前数组的长度,即需要扩容
        if (minimumCapacity - value.length > 0) {
            // 调用newCapacity方法,获取扩容后的容量
            // 将当前字符数组的内容复制到扩容后的新数组中,并返回新数组
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
	}

2)newCapacity方法

AbstractStringBuilder.java中的相关代码:

    private int newCapacity(int minCapacity) {
        // 计算新的容量为当前容量乘的2倍再加2
        int newCapacity = (value.length << 1) + 2;
        // 如果新计算的容量还是小于要求的容量
        if (newCapacity - minCapacity < 0) {
            // 设置新容量为要求的容量
            newCapacity = minCapacity;
        }
        // 如果新容量小于等于0或者新容量超过最大容量
        // 则返回调用hugeCapacity方法的结果,该方法用于获得一个相对较大的容量
        // 否则,返回新计算的容量
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
	}

3)hugeCapacity方法

AbstractStringBuilder.java中的相关代码:

	private int hugeCapacity(int minCapacity) {
    	// 若要求的容量大于整型数据的最大值,则抛出异常
        if (Integer.MAX_VALUE - minCapacity < 0) { 
            throw new OutOfMemoryError();
        }
        // 若要求的容量大于最大容量,则返回要求的容量,否则返回最大容量
        // 即返回两者中较大的一个
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
	}

6. trimToSize方法

该方法用于释放字符数组中没有用到空间。
AbstractStringBuilder.java中的相关代码:

	public void trimToSize() {
    	// 若已经使用的容量小于总容量
        if (count < value.length) {
            // 根据已经使用的容量,创建新的数组,将原数组的内容复制到新数组中
            // 保存新数组
            value = Arrays.copyOf(value, count);
        }
	}

7. setLength

该方法用于设置字符串的长度。
AbstractStringBuilder.java中的相关代码:

	public void setLength(int newLength) {
    	// 若设置的长度小于0,则抛出异常
        if (newLength < 0)
            throw new StringIndexOutOfBoundsException(newLength);
        // 进行扩容
        ensureCapacityInternal(newLength);

        // 如果已经使用的空间小于总空间
        if (count < newLength) {
            // 用’\0’对没有用到空间进行填充
            Arrays.fill(value, count, newLength, '\0');
        }
        // 设置已经使用使用的空间的数量
        count = newLength;
	}

8.charAt方法

获取字符串中指定位置的字符。
AbstractStringBuilder.java中的相关代码:

    @Override
	public char charAt(int index) {
    	// 若指定的位置小于0,或超过字符串的长度,则抛出异常
        if ((index < 0) || (index >= count))
            throw new StringIndexOutOfBoundsException(index);
        // 返回数组中对应位置的字符
        return value[index];
	}

9. getChars方法

将字符串中从srcBegin开始到srcEnd结束的字符填充到dst数组从dstBegin开始的位置。
AbstractStringBuilder.java中的相关代码:

    public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin){
    	// 若字符串起始位置小于0
        if (srcBegin < 0)
            throw new StringIndexOutOfBoundsException(srcBegin);
        // 若字符串终止位置小于0,或字符串终止位置超出了字符串的长度
        if ((srcEnd < 0) || (srcEnd > count))
            throw new StringIndexOutOfBoundsException(srcEnd);
        // 若字符串的起始位置超过了字符串的终止位置
        if (srcBegin > srcEnd)
            throw new StringIndexOutOfBoundsException("srcBegin > srcEnd");
        // 复制字符串的内容到数组中。
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
	}

10. setCharAt方法

将字符串中指定位置index处的字符设置为指定字符ch。
AbstractStringBuilder.java中的相关代码:

	public void setCharAt(int index, char ch) {
    	// 若起始位置小于0,或超过字符串最大长度
        if ((index < 0) || (index >= count))
            throw new StringIndexOutOfBoundsException(index);
        // 设置字符数组index处字符为ch
        value[index] = ch;
	}

11. append方法

向字符串末尾附加一个字符串形式的对象。

1)参数为String

AbstractStringBuilder.java中的相关代码:

	public AbstractStringBuilder append(String str) {
    	// 若附加的字符串为空
        if (str == null)
            // 则调用appendNull方法,附加”null”,并返回
            return appendNull();
        // 若附加的字符串不为空
        // 获取附加的字符串的长度
        int len = str.length();
        // 进行扩容,新字符串长度为原长度count+附加长度len
        ensureCapacityInternal(count + len);
        // 调用附加字符串的getChars方法,从源字符串末尾count位置开始添加
        str.getChars(0, len, value, count);
        // 设置新字符串的长度
        count += len;
        // 返回
        return this;
	}

参数为StringBuffer和AbstractStringBuilder时的处理过程也是如此。

a)appendNull方法

AbstractStringBuilder.java中的相关代码:

	private AbstractStringBuilder appendNull() {
    	int c = count;
    	// 扩容
    	ensureCapacityInternal(c + 4);
    	// 拼接”null”
    	final char[] value = this.value;
    	value[c++] = 'n';
    	value[c++] = 'u';
    	value[c++] = 'l';
    	value[c++] = 'l';
    	// 设置新字符串的长度
    	count = c;
    	// 返回
    	return this;
	}

2)参数为Object

AbstractStringBuilder.java中的相关代码:

	public AbstractStringBuilder append(Object obj) {
    	// 调用Object对象的toString方法,拼接
        return append(String.valueOf(obj));
	}

3)参数为char[]

AbstractStringBuilder.java中的相关代码:

	public AbstractStringBuilder append(char[] str) {
    	// 获取附加的字符串的长度
        int len = str.length;
        // 扩容
        ensureCapacityInternal(count + len);
        // 将str中长度为len的内容从起始位置0复制到value数组count后
        System.arraycopy(str, 0, value, count, len);
        // 设置新字符串的长度
        count += len;
        // 返回
        return this;
	}

4)参数为boolean

AbstractStringBuilder.java中的相关代码:

	public AbstractStringBuilder append(boolean b) {
    	// 为true
        if (b) {
            // 容量加4
            ensureCapacityInternal(count + 4);
            // 拼接”true”
            value[count++] = 't';
            value[count++] = 'r';
            value[count++] = 'u';
            value[count++] = 'e';
        } else {// 为false
            // 容量加5
            ensureCapacityInternal(count + 5);
            // 拼接”fasle”
            value[count++] = 'f';
            value[count++] = 'a';
            value[count++] = 'l';
            value[count++] = 's';
            value[count++] = 'e';
        }
        // 返回
        return this;
	}

5)参数为char

AbstractStringBuilder.java中的相关代码:

    @Override
	public AbstractStringBuilder append(char c) {
    	// 容量加1
        ensureCapacityInternal(count + 1);
        // 拼接字符
        value[count++] = c;
        // 返回
        return this;
	}

6)参数为int

AbstractStringBuilder.java中的相关代码:

	public AbstractStringBuilder append(int i) {
    	// 若为整型的最小值,即-2147483648
        if (i == Integer.MIN_VALUE) {
            // 直接拼接
            append("-2147483648");
            // 返回
            return this;
        }
        // 通过调用Integer的静态方法stringSize,获取i的长度
        int appendedLength = (i < 0) ? Integer.stringSize(-i) + 1
                                     : Integer.stringSize(i);
        // 计算拼接后的长度
        int spaceNeeded = count + appendedLength;
        // 扩容
        ensureCapacityInternal(spaceNeeded);
        // 调用Integer的getChars方法进行拼接
        Integer.getChars(i, spaceNeeded, value);
        // 设置新字符串长度
        count = spaceNeeded;
        返回
        return this;
	}

参数为long时,处理过程和int相似,只不过在拼接字符串时调用的是Long的静态方法getChars。

7)参数为float

AbstractStringBuilder.java中的相关代码:

	public AbstractStringBuilder append(float f) {
    	// 调用FloatingDecimal的静态方法appendTo方法进行拼接
        FloatingDecimal.appendTo(f,this);
        // 返回
        return this;
	}

参数double与float处理过程完全相同。

12. delete方法

删除字符串中指定开始位置和终止位置之间的字符。
AbstractStringBuilder.java中的相关代码:

	public AbstractStringBuilder delete(int start, int end) {
    	// 若起始位置小于0
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        // 若终止位置超过字符串长度
        if (end > count)
            // 设置终止位置为字符串末尾
            end = count;
        // 若起始位置大于终止位置
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        // 计算需要删除字符串的长度
        int len = end - start;
        // 若需要删除字符串的长度大于0
        if (len > 0) {
            // 将终止位置后面的字符串复制到起始位置后面
            System.arraycopy(value, start+len, value, start, count-end);
            // 设置删除后的字符串的长度
            count -= len;
        }
        // 返回
        return this;
	}

13. deleteCharAt方法

删除字符串中指定位置的字符。
AbstractStringBuilder.java中的相关代码:

	public AbstractStringBuilder deleteCharAt(int index) {
    	// 若指定位置小于0,或超过字符串长度
        if ((index < 0) || (index >= count))
            throw new StringIndexOutOfBoundsException(index);
        // 将index后面的字符串复制到index处
        System.arraycopy(value, index+1, value, index, count-index-1);
        // 新字符串长度为源字符串长度减1
        count--;
        // 返回
        return this;
	}

14.replace方法

将起始位置start和终止位置end之间的字符串替换为字符串str。
AbstractStringBuilder.java中的相关代码:

	public AbstractStringBuilder replace(int start, int end, String str) {
    	// 若起始位置小于0
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        // 若起始位置超出了字符串的长度
        if (start > count)
            throw new StringIndexOutOfBoundsException("start > length()");
        // 若起始位置大于终止位置
        if (start > end)
            throw new StringIndexOutOfBoundsException("start > end");
        // 若终止位置超出字符串长度
        if (end > count)
            // 设置终止位置为字符串的末尾
            end = count;
        // 获取需要替换进来的字符串的长度
        int len = str.length();
        // 计算替换后的长度:原来的长度+替换进来的长度-替换后被覆盖的长度
        int newCount = count + len - (end - start);
        // 扩容
        ensureCapacityInternal(newCount);
        
        // 从替换后被覆盖的位置开始将后面的字符复制到
        // 加入替换进来的字符串后的末尾
        // 把需要替换进来的字符串需要的空间留出来
        System.arraycopy(value, end, value, start + len, count - end);
        // 添加需要替换进来的字符
        str.getChars(value, start);
        // 设置新的字符串的长度
        count = newCount;
        // 返回
        return this;
    }

15. substring方法

截取指定起始位置和终止位置的字符串。
AbstractStringBuilder.java中的相关代码:

	public String substring(int start, int end) {
    	// 若起始位置小于0
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        // 若终止位置超过了字符串的长度
        if (end > count)
            throw new StringIndexOutOfBoundsException(end);
        // 若起始位置大于终止位置
        if (start > end)
            throw new StringIndexOutOfBoundsException(end - start);
        // 返回一个新的字符串
        return new String(value, start, end - start);
	}

16.insert方法

从指定位置开始向原字符串中插入一个字符串。
AbstractStringBuilder.java中的相关代码:

	public AbstractStringBuilder insert(int offset, String str) {
    	// 若起始位置小于0,或超过了字符串的长度
        if ((offset < 0) || (offset > length()))
            throw new StringIndexOutOfBoundsException(offset);
        // 若插入的字符串为空
        if (str == null)
            // 对其赋值为”null”
            str = "null";
        // 获取需要插入的字符串的长度
        int len = str.length();
        // 扩容
        ensureCapacityInternal(count + len);
        // 将原字符串中起始位置后面的字符复制到终止位置后面
        // 为插入的字符串留好空间
        System.arraycopy(value, offset, value, offset + len, count - offset);
        // 插入字符串
        str.getChars(value, offset);
        // 设置新字符串长度
        count += len;
        // 返回
        return this;
	}

17.indexOf方法

查找字符串中是否包含指定的字符串,若包括,则返回字符串的起始位置,否则返回-1。
AbstractStringBuilder.java中的相关代码:

    public int indexOf(String str) {
        return indexOf(str, 0);
	}

调用了重载方法。
AbstractStringBuilder.java中的相关代码:

    public int indexOf(String str, int fromIndex) {
        return String.indexOf(value, 0, count, str, fromIndex);
	}

调用了String中的静态方法indexOf,该方法用于指定位置查找在一个字符数组是否包含另一个字符数组。且这两个数组都可以选择起始位置和长度。(终止位置=起始位置+长度)
String.java中的相关代码:

    static int indexOf(char[] source, int sourceOffset, int sourceCount,
            char[] target, int targetOffset, int targetCount,
            int fromIndex) {
        // 若查找的起始位置大于等于被查找的字符串长度
        if (fromIndex >= sourceCount) {
            // 若查找的字符串的长度为0,说明查找的字符串为””,
			// 则返回被查找字符串的长度,否则返回-1
            return (targetCount == 0 ? sourceCount : -1);
        }
        // 若查找的起始位置小于0
        if (fromIndex < 0) {
            // 设置查找的起始位置为0
            fromIndex = 0;
        }
        // 若查找的字符串长度为0,说明查找的字符串为””
        if (targetCount == 0) {
            // 返回查找的起始位置
            return fromIndex;
        }

        // 获取需要查找的第一个字符
        char first = target[targetOffset];
        // 计算循环在被查找字符串停止的位置
        // sourceCount – targetCount表示起始位置为0时,查找字符串需要停止的位置
        // 因为继续查找,被查找字符串剩下的长度小于需要查找的字符串长度
        // 由于被查找的字符串起始位置不为0,所以要加上sourceOffset
        int max = sourceOffset + (sourceCount - targetCount);

        // 循环
		// 起始位置为被查找字符的起始位置加上查找的起始位置(偏移)
        for (int i = sourceOffset + fromIndex; i <= max; i++) {
            // 若被查找字符串和需要查找的字符串的第一个字符不相同
            if (source[i] != first) {
                // 循环,找到第一个相同的字符,
				// 同时首位相同的字符位置i不能超过最大位置max
                while (++i <= max && source[i] != first);
            }

            // 若首位相同的字符位置i不能超过最大位置max
            // 则继续找剩余的字符
            if (i <= max) {
                // 计算之后需要查找的字符的起始位置
                int j = i + 1;
                // 计算最后一个需要查找的字符的位置
                // j-1+tartgetCount = i + targetCount
                int end = j + targetCount - 1;
                // 循环,查找剩余字符是否相等,不相等就结束循环
                for (int k = targetOffset + 1; j < end && source[j]
                        == target[k]; j++, k++);

                // 判断上面的循环是否循环到了最后
                // 循环到最后说明剩下所有的字符都已经找到,即j == end
                // 若找到
                if (j == end) {
                    // 返回需要查找的字符串的首字符在
					// 被查找字符串中出现的起始位置
                    return i - sourceOffset;
                }
            }
        }
        // 若没有找到,返回-1
        return -1;
    }

18. reverse方法

对字符串进行反转。
AbstractStringBuilder.java中的相关代码:

	public AbstractStringBuilder reverse() {
    	// 表示字符串中的字符是否是UTF-16中用于构成Unicode字符的保留字符
    	// 即是否是两个字符为一个Unicode字符
        boolean hasSurrogates = false;
        // n为字符数组最后一个字符的位置
        int n = count - 1;
        // 循环 从中间开始,两个指针j和k,依次向两边走,交换字符
        // 长度为偶数时,从最中间两个字符开始
        // 长度为奇数时,最中间的字符会被跳过,会从中间字符的两边开始
        // (n-1)除以2后的到的值为我们要的中间的值
        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;
            // 判断字符是否为UTF-16中用于构成Unicode字符的保留字符
            if (Character.isSurrogate(cj) ||
                Character.isSurrogate(ck)) {
                // 若是,则设置标志位为true
                hasSurrogates = true;
            }
        }
        // 如果是保留字符
        if (hasSurrogates) {
            // 以两个字符为一个单位,调换位置
            // 因为高低代理对的顺序不能变 
            reverseAllValidSurrogatePairs();
        }
        // 返回
        return this;
	}

1)reverseAllValidSurrogatePairs方法

AbstractStringBuilder.java中的相关代码:

	private void reverseAllValidSurrogatePairs() {
    	// 循环
        for (int i = 0; i < count - 1; i++) {
            // 获取一代理对中的第一个
            char c2 = value[i];
            // 如果是低代理
            if (Character.isLowSurrogate(c2)) {
                // 获取代理对中第二个
                char c1 = value[i + 1];
                // 如果为高代理
                if (Character.isHighSurrogate(c1)) {
                    // 交换位置
                    value[i++] = c1;
                    value[i] = c2;
                }
            }
        }
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值