文/小图灵视界
本头条号将会继续分析JDK8的源码,欢迎关注和收藏,也会将分析笔记开源。
位置:java.lang
大家都知道Java中的String是不可变的,StringBuffer类和StringBuilder类是可变的。而AbstractStringBuilder类是StringBuffer类和StringBuilder类的父类,也是可以可变的类,AbstractStringBuilder类的定义如下:
abstract class AbstractStringBuilder implements Appendable, CharSequence
AbstractStringBuilder类是抽象类,实现了Appendable和CharSequence接口,可进行添加操作和CharSequence序列相关操作。Appendable接口的方法如下:
//将序列添加在字符串上
Appendable append(CharSequence csq) throws IOException;
//从start位置到end位置的csq子字符添加到字符串
Appendable append(CharSequence csq, int start, int end) throws IOException;
//将字符c添加到字符串中
Appendable append(char c) throws IOException;
属性
//用来保存字符的char数组
char[] value;
//表示有多少用了多少容量(字符的个数),表示value数组中存储字符的数量
int count;
//AbstractStringBuilder类最大数组长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
AbstractStringBuilder类是抽象类,所以它的所有属性默认都是使用public修饰,value属性用来保存字符串,count表示value数组中存储字符的数量。与String中value不同,AbstractStringBuilder的value数组是可以改变的。MAX_ARRAY_SIZE是可容纳的最大字符的长度。
构造器
//没有参数的构造器
AbstractStringBuilder() {}
//给定容量的构造器
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
AbstractStringBuilder类有两个构造器,一个无参构造器,一个给定容量capacity的构造器,这个构造器创建capacity大小的char数组。
常用方法
//返回value数组中存储字符的数量
public int length() {
return count;
}
//返回value数组的容量大小
public int capacity() {
return value.length;
}
length() 方法返回的是value数组中存储字符的实际数量, capacity() 返回的是value数组的容量大小。当value数组中都存储满了字符,那么length()和capacity()返回的值相等,否则,capacity()的返回值大于length() 的返回值。
//扩容
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0)
ensureCapacityInternal(minimumCapacity);
}
//实际扩容方法
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,newCapacity(minimumCapacity));
}
}
ensureCapacity(int minimumCapacity)方法的作用是扩容数组,当minimumCapacity大于0时,调用ensureCapacityInternal(int minimumCapacity)方法。
当ensureCapacityInternal(int minimumCapacity)方法的minimumCapacity参数大于value数组的长度时,通过Arrays.copyOf方法复制字符串到扩容的新的数组中,然后再赋值给value数组。
在调用Arrays.copyOf方法时,调用了newCapacity(int minCapacity)方法,这个方法就是实际扩容数组大小的的方法。newCapacity(int minCapacity)定义如下:
//返回新的数组长度的大小
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;
}
private int hugeCapacity(int minCapacity) {
if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE)? minCapacity : MAX_ARRAY_SIZE;
}
newCapacity(int minCapacity)方法首先将原来char数组value的长度扩大2倍加2((value.length << 1) + 2),得到新的长度newCapacity,当newCapacity小于传入的参数minCapacity时,将minCapacity赋值给newCapacity。当newCapacity小于等于0或者newCapacity大于MAX_ARRAY_SIZE时,调用 hugeCapacity(int minCapacity) 方法扩容,否则返回newCapacity。
hugeCapacity(int minCapacity)方法,当minCapacity大于Integer.MAX_VALUE时,抛出越界OutOfMemoryError异常,如果minCapacity大于MAX_ARRAY_SIZE,返回minCapacity,否则返回MAX_ARRAY_SIZE。
public void trimToSize() {
if (count < value.length) {
value = Arrays.copyOf(value, count);
}
}
trimToSize()方法调整value数组的容量大小,当数组value中保存的实际字符大小count小于数组value的容量时,将value数组中的大小复制给容量大小为count的新创建的数组中,然后将新的数组赋值给value,这时capacity()方法返回的值和 length()返回的值一样大小。这个方法可能会改变capacity()方法返回的值,即原来value数组的容量大小。
public void setLength(int newLength) {
//如果是负数,抛出异常
if (newLength < 0)
throw new StringIndexOutOfBoundsException(newLength);
//newLength大于value.length时,复制数组进行扩容
ensureCapacityInternal(newLength);
//当数组存储字符的数量count小于newLength,对数组填充'\0'
if (count < newLength) {
Arrays.fill(value, count, newLength, '\0');
}
//将当数组存储字符的数量count设置为newLength
count = newLength;
}
setLength(int newLength) 方法设置字符序列的长度。当newLength参数小于字符串value数组的长度,数组存储字符的数量count被设置为newLength。当newLength参数大于字符串value数组的长度,首先调用ensureCapacityInternal方法对字符串的原数组value进行扩容,然后判断下数组存储字符的数量count是否小于newLength,如果是的话,将数组count索引以后的位置用‘\0’填充,最后将count设置为newLength。
public char charAt(int index) {
//边界判断
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
//返回指定索引位置的值
return value[index];
}
charAt(int index)方法的作用是返回指定索引位置的字符。首先进行边界判断,如果index小于0或者index大于等于数组value实际存储字符的数量count,则抛出StringIndexOutOfBoundsException异常。当index合法时,直接通过数组取index位置的值。
public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin){
if (srcBegin < 0)
throw new StringIndexOutOfBoundsException(srcBegin);
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);、
}
getChars方法的作用是将字符串的字符复制到指定的char数组中。getChars方法将value数组的srcBegin位置到srcEnd位置的字符串复制到char数组dst中dstBegin开始的位置。方法首先进行边界判断,当srcBegin小于0、srcEnd或者srcEnd大于count、srcBegin大于srcEnd时,抛出StringIndexOutOfBoundsException异常。当srcBegin、srcEnd、dstBegin都合法时,通过本地方法System.arraycopy进行复制字符到dst数组中。
public void setCharAt(int index, char ch) {
//如果index小于0或者index大于等于count
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
value[index] = ch;
}
setCharAt方法的作用是将字符串某个index位置的值设置为给定的字符。/如果index小于0或者index大于等于实际存储字符的数量count,抛出StringIndexOutOfBoundsException异常。最后将字符ch直接赋值给value数组的index位置。
public AbstractStringBuilder append(String str) {
//添加空字符串
if (str == null)
return appendNull();
int len = str.length();
//当count + len大于value.length时,进行扩容
ensureCapacityInternal(count + len);
//将str字符串复制到value数组中
str.getChars(0, len, value, count);
//数组实际存储字符的数量count添加str的长度
count += len;
return this;
}
private AbstractStringBuilder appendNull() {
int c = count;
//当c+4大于value.length时,进行扩容
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;
}
append(String str)方法将str字符串添加到原来字符串后面。如果参数str为null,执行appendNull()方法,appendNull方法首先将数组进行扩容,将空字符串“null”添加到数组中,当参数str不为null时,首先判断是否需要对数组进行扩容,当count + len大于value.length时,进行数组扩容。然后将str字符串复制到value数组中,更新数组实际存储字符的数量count,count的大小等于原来count的大小加上str字符串的长度len。
public AbstractStringBuilder append(StringBuffer sb) {
if (sb == null)
return appendNull();
int len = sb.length();
ensureCapacityInternal(count + len);
sb.getChars(0, len, value, count);
count += len;
return this;
}
AbstractStringBuilder append(AbstractStringBuilder asb) {
if (asb == null)
return appendNull();
int len = asb.length();
ensureCapacityInternal(count + len);
asb.getChars(0, len, value, count);
count += len;
return this;
}
append(StringBuffer sb)方法、append(AbstractStringBuilder asb)方法和append(String str)方法的逻辑一模一样,没什么好分析的了。
public AbstractStringBuilder append(CharSequence s) {
//添加空字符null
if (s == null)
return appendNull();
if (s instanceof String)
//当参数是String类型
return this.append((String)s);
if (s instanceof AbstractStringBuilder)
//当参数是AbstractStringBuilder类型
return this.append((AbstractStringBuilder)s);
//一般的序列类型(CharSequence)
return this.append(s, 0, s.length());
}
//实现Appendable接口的 append(CharSequence s, int start, int end)方法
public AbstractStringBuilder append(CharSequence s, int start, int end) {
//空字符“null”
if (s == null)
s = "null";
//边界判断
if ((start < 0) || (start > end) || (end > s.length()))
throw new IndexOutOfBoundsException(
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start;
//当count + len大于value.length时,进行扩容
ensureCapacityInternal(count + len);
//遍历序列s,将序列的字符从start位置到end复制到value数组的count位置后
for (int i = start, j = count; i < end; i++, j++)
value[j] = s.charAt(i);
count += len;
return this;
}
append(CharSequence s)方法是将序列添加到字符串的后面,参数是CharSequence类型,当序列s为nulll时,调用appendNull()方法进行空字符“null”的添加;当序列s为String类型时,调用append(String str)方法;当序列s为AbstractStringBuilder时,调用append(AbstractStringBuilder asb)方法;当序列s只是一般的序列时,调用 append(CharSequence s, int start, int end)方法,该方法为Appendable接口的方法。
append(CharSequence s, int start, int end)方法针对一般的序列添加操作,首先判断序列s是否为null,然后进行边界判断,当count + len大于value.length时,进行数组扩容,最后遍历序列s,将序列的字符从start位置到end复制到value数组的count位置后,更新count的大小。
public AbstractStringBuilder append(char[] str) {
int len = str.length;
//当count + len大于value.length时,进行扩容
ensureCapacityInternal(count + len);
//数组复制
System.arraycopy(str, 0, value, count, len);
//更新count的大小
count += len;
return this;
}
//从chat数组str的offset位置复制len个字符到value数组
public AbstractStringBuilder append(char str[], int offset, int len) {
if (len > 0) // let arraycopy report AIOOBE for len < 0
ensureCapacityInternal(count + len);
System.arraycopy(str, offset, value, count, len);
count += len;
return this;
}
append(char[] str)方法将char数组添加在字符串的后面。首先当count + len大于value.length时,进行数组的扩容,然后利用System.arraycopy方法将char数组str复制给value,最后更新数组实际存储字符的数量count。
//删除从start位置到end位置的字符串
public AbstractStringBuilder delete(int start, int end) {
//边界判断
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
//如果end大于count,将count赋值给end
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
//将value数组的start+len位置开始覆盖value的数组的start位置,覆盖count-end个字符
System.arraycopy(value, start+len, value, start, count-end);
count -= len;
}
return this;
}
//删除index位置的字符
public AbstractStringBuilder deleteCharAt(int index) {
//边界判断
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
System.arraycopy(value, index+1, value, index, count-index-1);
count--;
return this;
}
delete(int start, int end) 方法删除从start位置到end位置的字符。如果start小于0,抛出异常,如果end大于count,将count赋值给end。当start > end时,抛出异常。最后通过System.arraycopy复制方式将start位置到end位置的字符被start+len位置开始的字符覆盖,将count-end个字符往前移动,最后更新数组实际存储字符的数量count。deleteCharAt(int index)方法逻辑跟delete(int start, int end) 逻辑是一样的,都是通过System.arraycopy将某位置的字符覆盖前面的字符。
//将start到end位置的字符串替换为字符串str
public AbstractStringBuilder replace(int start, int end, String str) {
//边界判断
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();
//新的数组实际存储字符的数量count
int newCount = count + len - (end - start);
//如果newCount大于value.length,进行数组扩容
ensureCapacityInternal(newCount);
//将end位置以后的字符串从start + len位置覆盖value数组,
//相当于空出len长度的位置,用来添加str字符
System.arraycopy(value, end, value, start + len, count - end);
//将str字符添加在value数组中
str.getChars(value, start);
//更新count
count = newCount;
return this;
}
replace(int start, int end, String str) 方法的作用将start到end位置的字符串替换为字符串str。首先进行边界判断,计算出新的数组实际存储字符的数量newCount,如果newCount大于value.length,进行数组扩容,然后通过System.arraycopy将end位置以后的字符串从start + len位置开始覆盖value数组,相当于空出len长度的位置,用来添加str字符。最后将str字符添加在value数组中,更新count。
public AbstractStringBuilder insert(int index, char[] str, int offset,int len){
if ((index < 0) || (index > length()))
throw new StringIndexOutOfBoundsException(index);
if ((offset < 0) || (len < 0) || (offset > str.length - len))
throw new StringIndexOutOfBoundsException(
"offset " + offset + ", len " + len + ", str.length "
+ str.length);
//当count + len大于value.length时,进行扩容
ensureCapacityInternal(count + len);
//将index位置后的元素移动到index + len位置,移动count - index长度的元素
//相当于在index位置以后空出len长度的位置添加str字符
System.arraycopy(value, index, value, index + len, count - index);
//在空出的len位置上添加str字符到value数组中
System.arraycopy(str, offset, value, index, len);
//更新count
count += len;
return this;
}
insert(int index, char[] str, int offset,int len)方法的作用是从某个位置插入char数组,index表示插入char数组的位置,str吧表示插入的char数组,offset是str数组开始插入value数组的位置,len表示需要插入长度。方法首先进行边界判断,当count + len大于value.length时,进行数组扩容,然后通过System.arraycop将index位置后的元素移动到index + len位置,移动count - index长度的元素,相当于在index位置以后空出len长度的位置添加str字符,再次通过System.arraycopy在空出的len位置上添加str字符到value数组中,最后更新count。
//从offset位置插入str字符串
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();
//如果count + len大于value.length时,进行数组扩容
ensureCapacityInternal(count + len);
//将offset位置开始的元素移动到offset + len开始的位置上,相当于空出len长度来插入str字符串
System.arraycopy(value, offset, value, offset + len, count - offset);
//将str字符串复制给value数组
str.getChars(value, offset);
//更新count
count += len;
return this;
}
insert(int offset, String str)方法的作用从offset位置插入str字符串。如果count + len大于value.length时,进行数组扩容,通过System.arraycopy方法将offset位置开始的元素移动到offset + len开始的位置上,相当于空出len长度来插入str字符串,然后通过将getChars方法将str字符串复制给value数组,最后更新count。
//从offset位置插入char数组str
public AbstractStringBuilder insert(int offset, char[] str) {
if ((offset < 0) || (offset > length()))
throw new StringIndexOutOfBoundsException(offset);
int len = str.length;
//扩容
ensureCapacityInternal(count + len);
//offset位置字符移动到offset + len位置,移动count - offset个元素
//相当于空出len长度的位置添加str数组
System.arraycopy(value, offset, value, offset + len, count - offset);
//在空出的len长度添加str数组
System.arraycopy(str, 0, value, offset, len);
//更新count
count += len;
return this;
}
insert(int offset, char[] str) 方法的作用是将从offset位置插入char数组str。如果count + len大于value.length时,进行数组扩容。然后通过System.arraycopy将offset位置字符移动到offset + len位置,移动count - offset个元素相当于空出len长度的位置添加str数组,再次通过System.arraycopy在空出的len长度添加str数组,最后更新count。
//由子类实现
public abstract String toString();
//获取value数组的方法
final char[] getValue() {
return value;
}