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;
}
}
}
}