1. class 简介
StringBuilder类和StringBuffer类的功能几乎一模一样,除了后者是多线程安全的,而前者主要用于单线程环境,是从性能考虑上对StringBuffer的替代。它们最主要的操作是拼接和插入,还附带转置和删除字符串的操作,String类的一些方法StringBuilder和StringBuffer多少也有一些,但是这两个类的任何操作都是在原字符串上进行的。它们都从AbstractStringBuilder类继承而来,大部分的操作都发生在父类。
2. class内部原理及特点
- 在其父类AbstractStringBuilder中维护了一个字符数组,和String不同的是,没有final修饰,所以是可变的,默认容量为16,传入字符串进行初始化时容量为其长度增加16。
- 扩容时总容量*2+2,如果容量仍然不够用,则直接扩充为要求长度。
- 拼接和插入时都使用System.arraycopy,除非扩容时可能产生新的字符数组,一般情况不会出现中间对象,所以效率比String类要高。
3. class源码细节分析
StringBuilder和StringBuffer类的核心都在其父类AbstractStringBuilder类中,所以只需要看AbstractStringBuilder类中的一些具有代表性的方法。
expandCapacity
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;//容量*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);
}
append
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);//看下要不要扩容
str.getChars(0, len, value, count);//把参数str复制到value字符数组中,整个拼接过程可能不会产生新对象
count += len; //性能必然比String的+和concat要好
return this;
}
其它类型的拼接都差不多,不是字符类型的先转换成字符,再复制到value中。
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);//是否需要扩容
System.arraycopy(value, offset, value, offset + len, count - offset);//插入点后的字符往后挪,腾出空间来
str.getChars(value, offset); //把待插入字符串复制到offset插入点
count += len;
return this;
}
其它类型数据的插入都是大同小异,没有特别的地方。
delete
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) {
System.arraycopy(value, start+len, value, start, count-end);//把后面的往前面覆盖
count -= len;
}
return this;
}
reverse
public AbstractStringBuilder reverse() {
boolean hasSurrogate = false;
int n = count - 1;
for (int j = (n-1) >> 1; j >= 0; --j) {//从中间位置开始
char temp = value[j];
char temp2 = value[n - j];
if (!hasSurrogate) {
hasSurrogate = (temp >= Character.MIN_SURROGATE && temp <= Character.MAX_SURROGATE)
|| (temp2 >= Character.MIN_SURROGATE && temp2 <= Character.MAX_SURROGATE);
}
value[j] = temp2;//左半边和右半边交换位置
value[n - j] = temp;
}
if (hasSurrogate) {//代理对的问题跳过
// Reverse back all valid surrogate pairs
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;
}
}
}
}
return this;
}
4. 总结
如果要对字符串进行频繁的操作,StringBuilder和StringBuffer的性能肯定要更好,或许在转换成StringBuilder或StringBuffer对象时会多一些开销,但是和频繁操作中带来的优势相比,这点就不足为虑了;StringBuilder比StringBuffer使用的更加广泛,毕竟多线程操作同一个字符串的情况实在少见。