1.介绍
1.1 String是一个固定的字符对象一旦初始化完就不可改变。而StringBuild和StringBuffer是java里处理可变的字符串。StringBuild不是现场安全的,StringBuffer是现场安全的。
1.2 主要方法,主要有四类,尾部追加append(),插入insert(),删除delete0及分割字串substring()等、
1.3实现原理
StringBuild继承与抽象类AbstractStringBuilder。大部分暴露的方法的实现都是调用AbstractStringBuilder对象的里方法。
StringBuffer继承与抽象类AbstractStringBuilder。大部分暴露的方法的实现都是调用AbstractStringBuilder对象里类方法。由于StringBuffer是现场安全的,所以它的大部分方法都使用了synchronized保证同步。
例如如下分别是StringBuild类和StringBuffer类里的的append方法。
//StringBuilder类里的append方法
public StringBuilder append(String str) {
append0(str);
return this;
}
//StringBuffer类里的append方法
public synchronized StringBuffer append(Stringstring) {
append0(string);
return this;
}
通过源码我们很清楚的看到,StringBuild和StringBuffer实现基本都是使用的AbstractStringBuilder,区别就是有没有使用synchronized区别。我们只要研究AbstractStringBuilder类里实现,就可以学习到可变字符串的实现原理了
2.AbstractStringBuilder主要方法介绍
2.1)AbstractStringBuilder类思想
其实就是有字符数组的成员变量[char[]value],一个记录长度的成员变量[intcount],默认容量就是16。实现原理就是插入新东西的时候,判断剩余容量是否装载的下插入的字符类型,不够的话扩容量,把当前字符数组容量扩大1.5倍,把旧的字符串复制进来,再把新的字符串复制进去;够的话直接插入就行。
下面就分别通过源码解读几个不同类型的几个经典方法,分析具体的实现思想、
2.2)三个构造函数
AbstractStringBuilder()
//构造容量默认是16的AbstractStringBuilder对象
AbstractStringBuilder(int capacity)
//构造容量是capacity的AbstractStringBuilder对象
AbstractStringBuilder(String string)
//构造内容是string的AbstractStringBuilder对象
2.3)尾部追加的方法:append0(char[] chars)
原理:追加字符数组的时候,判断剩余容量是否够装载追加的串,够的话直接插入,不够的话,扩容1.5倍,在把旧串复制进来,把新串追加到尾部。
final void append0(char[] chars) {
//获取到需要的总长度
int newCount = count + chars.length;
//总长度超过字符数组的长度,扩容量
if (newCount > value.length) {
//扩容方法下面有介绍
enlargeBuffer(newCount);
}
//插入新的字符数组到原始的value字符数组里
System.arraycopy(chars, 0, value, count, chars.length);
count = newCount;
}
private void enlargeBuffer(int min) {
//当前数组扩容1.5倍
int newCount = ((value.length >> 1) + value.length) + 2;
//如果扩容后的newCount还是小于min。使用min大小,否则使用newCount大小
char[] newData = new char[min > newCount ? min : newCount];
//把旧的字符数字复制进来
System.arraycopy(value, 0, newData, 0, count);
//重写对成员变量赋值
value = newData;
shared = false;
}
举例:给字符数组长度是5,内容是stri里插入字符串ab,判断剩余容量是1,装不下ab,直接容量扩大1.5倍,变成7,把旧的串复制进来,同时在把ab复制进来。
2.4)合适位置插入新串的方法:insert0(int index, char[] chars)
原理: 首先判断容量够不,够的话,调用move方法,移动字符数据的内容。
其实就是把index到lenght的位置字符移动到index+size[新插入字符的长度]位置以后,预留出来index到index+size的位置插入新串即可;如果容量不够,扩容1.5倍,先把0-index位置的字符串复制进来,再把index到length位置的字符串复制到
index+size以后,最后再把新的串插入到index到index+size的位置。
final void insert0(int index, char[]chars) {
//异常下标处理
if (0 > index || index > count) {
throw new StringIndexOutOfBoundsException(index);
}
//字符数组有内容,执行插入
if (chars.length != 0) {
//合理扩容及移动字符串,留出来合适位置插入新串
move(chars.length, index);
//把新的串插入到index位置即可
System.arraycopy(chars, 0, value, index, chars.length);
count += chars.length;
}
}
private void move(int size, int index) {
int newCount;
if (value.length - count >= size) {
//数组容量够转下新的串,把旧的串从index到count位置的字串复制到value里的 index + size位置以后,留出来的index到index + size位置刚好复制新插入的串
if (!shared) {
System.arraycopy(value, index,value, index + size, count
- index); // index ==count case is no-op
return;
}
newCount = value.length;
} else {
//扩容量
int a = count + size, b = (value.length << 1) + 2;
newCount = a > b ? a : b;
}
char[] newData = new char[newCount];
//把旧串0到index位置的串复制到新的数组value里
System.arraycopy(value, 0, newData, 0, index);
// 复制旧串index到count位置到新串index + size位置以后,留出来的index到index + size位置刚好复制新插入的串
System.arraycopy(value, index, newData, index + size, count - index);
value = newData;
shared = false;
}
举例:给一个字符串长度是5,内容是abcde,容量是9的字符数组里,在2的位置插入gh。
1) 判断容量够,不用扩容,直接把2[想插入的起始位置]-4[旧字符串的长度]。的字符串整齐向后两个位置复制。变成abcdcde,中间留了两个位置2[想插入的起始位置]-4[插入起始位置加上插入的串长度]。
2) 直接在2-4的位置覆盖插入gh即可。
2.5:总结
其它类似于字符串的分割方法substring()、替换方法 replace0()、删除方法delete0(),要不较为简单,要不实现类似,就不在介绍。学习和了解源码的实现思路,学习到核心思想和编程要点就最重要的。