JDK11
代码参考的是 https://github.com/kangjianwei/LearningJDK/blob/master/src/java/lang/String.java
AbstractStringBuilder
这个类方法挺多的,1700多行,只挑一些重要而且陌生的记录下
/**
* A mutable sequence of characters.
* <p>
* Implements a modifiable string. At any point in time it contains some
* particular sequence of characters, but the length and content of the
* sequence can be changed through certain method calls.
*
* <p>Unless otherwise noted, passing a {@code null} argument to a constructor
* or method in this class will cause a {@link NullPointerException} to be
* thrown.
*
* @author Michael McCloskey
* @author Martin Buchholz
* @author Ulf Zibis
* @since 1.5
*/
// 字符序列的抽象实现,是StringBuilder和StringBuffer的父类
abstract class AbstractStringBuilder implements Appendable, CharSequence
1.从注释可以得知,AbstractStringBuilder的字符序列是可变的。
2.这是一个抽象类,子类是StringBuilder和StringBuffer
- 成员
/**
* The maximum size of array to allocate (unless necessary).
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* The value is used for character storage.
*/
// 以字节形式存储字符序列
byte[] value;
/**
* The id of the encoding used to encode the bytes in {@code value}.
*/
// 当前字符序列的编码:LATIN1或UTF16,由此可将ASB分为LATIN1-ASB或UTF16-ASB两类
byte coder;
/**
* The count is the number of characters used.
*/
// 当前ASB内包含的char的数量
int count;
- constructor
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
// 构造指定容量的ASB,内容为空
AbstractStringBuilder(int capacity) {
if(String.COMPACT_STRINGS) {
value = new byte[capacity];
coder = String.LATIN1;
} else {
value = StringUTF16.newBytesFor(capacity);
coder = String.UTF16;
}
}
- append方法
方法太多了,但是都是函数签名不同,大同小异。
/**
* Documentation in subclasses because of synchro difference
*/
// 向ASB末尾添加一个字符序列
@Override
public AbstractStringBuilder append(CharSequence s) {
if(s == null) {
return appendNull();
}
if(s instanceof String) {
return this.append((String) s);
}
if(s instanceof AbstractStringBuilder) {
return this.append((AbstractStringBuilder) s);
}
return this.append(s, 0, s.length());
}
1.如果传进来的字符序列是空,调用以下函数
// 添加一个字符串:"null"
private AbstractStringBuilder appendNull() {
ensureCapacityInternal(count + 4);
int count = this.count;
byte[] val = this.value;
if(isLatin1()) {
val[count++] = 'n';
val[count++] = 'u';
val[count++] = 'l';
val[count++] = 'l';
} else {
// 将4个char依次存入UTF16-SB内部的字节
count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l');
}
this.count = count;
return this;
}
2.根据字符序列的类型(String或者是AbstractStringBuilder)调用append并对s作强制类型转换
- 逆序
LATIN1:交换对称位置的byte
UTF_16:byte转字节再交换
public AbstractStringBuilder reverse() {
byte[] val = this.value;
int count = this.count;
int coder = this.coder;
int n = count - 1;
if(String.COMPACT_STRINGS && coder == String.LATIN1) {
//k和j为对称位置
for(int j = (n - 1) >> 1; j >= 0; j--) {
int k = n - j;
byte cj = val[j];
val[j] = val[k];
val[k] = cj;
}
} else {
StringUTF16.reverse(val, count);
}
return this;
}
- 比较
int compareTo(AbstractStringBuilder another) {
if(this == another) {
return 0;
}
byte val1[] = value;
byte val2[] = another.value;
int count1 = this.count;
int count2 = another.count;
if(coder == another.coder) {
return isLatin1() ? StringLatin1.compareTo(val1, val2, count1, count2) : StringUTF16.compareTo(val1, val2, count1, count2);
}
return isLatin1() ? StringLatin1.compareToUTF16(val1, val2, count1, count2) : StringUTF16.compareToLatin1(val1, val2, count1, count2);
}
1.先比较传进来的ASB与自身引用
2.本质上是比较字节数组的元素是否相同,在此之前先要对比两个ASB的编码方式
3.如果是Latin1则直接按字节比较,如果是UTF-16则先要把字节转换成字符
- 删除操作
// 删除[start, end)范围内的char
public AbstractStringBuilder delete(int start, int end) {
int count = this.count;
if(end>count) {
end = count;
}
checkRangeSIOOBE(start, end, count);
int len = end - start; // 计算删除元素的数量
if(len>0) {
// 从end处的char开始,将后续所有的char平移-len个单位,负数则左移,正数右移
shift(end, -len);
this.count = count - len;
}
return this;
}
1.如果end大于ASB的字节数量的话,那当然首先要把end设成当前最大的字节数量避免越界
2.然后核心思想是从end处的char开始,将后续所有的char平移-len个单位
调用的是arrayCopy这个本地方法:
数组复制,从src的srcPos索引处复制length个元素放入dest的destPos索引处
private void shift(int offset, int n) {
System.arraycopy(value, offset << coder, value, (offset + n) << coder, (count - offset) << coder);
}
- 删除索引为Index的字符串
// 删除索引为index的char
public AbstractStringBuilder deleteCharAt(int index) {
String.checkIndex(index, count);
// 从index+1处的char开始,将后续所有的char平移-1个单位,即删除一个cahr
shift(index + 1, -1);
count--;
return this;
}
- 插入操作
方法也很多,这里只选一个
// 向ASB的offset索引处插入一个字符串str
public AbstractStringBuilder insert(int offset, String str) {
String.checkOffset(offset, count);
if(str == null) {
str = "null";
}
int len = str.length();
ensureCapacityInternal(count + len);
shift(offset, len);
count += len;
// 向ASB的offset索引处插入一个字符串str
putStringAt(offset, str);
return this;
}
一个严谨的函数,要对所有的参数都做异常判断处理:
这里的checkOffSet和字符串的判空,ensureCapacityInternal扩容函数都是好例子
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
int oldCapacity = value.length >> coder;
// 扩容
if(minimumCapacity - oldCapacity>0) {
// 本质是新创建一个容量为newCapacity
value = Arrays.copyOf(value, newCapacity(minimumCapacity) << coder);
}
}
至于实现的核心思想就是用shift函数把字节数组中offset后的字节右移要插入的字符串长度位
然后调用putStringAt的getBytes函数
// 向ASB的index索引处插入一个字符串str
private final void putStringAt(int index, String str) {
if(getCoder() != str.coder()) {
inflate();
}
str.getBytes(value, index, coder);
}
// 拷贝String中的字节到dst数组
void getBytes(byte dst[], int dstBegin, byte coder) {
if(coder() == coder) {
// src dst
System.arraycopy(value, 0, dst, dstBegin << coder, value.length);
} else {
/* 如果两个coder不同,则将源字符串当做LATIN-String对待 */
// 从LATIN-String内部的字节转为UTF16-String内部的字节
StringLatin1.inflate(value, 0, dst, dstBegin, value.length);
}
}
- 求子串
// 求ASB在[start, start+end)范围内的子串
public String substring(int start, int end) {
checkRangeSIOOBE(start, end, count);
if(isLatin1()) {
return StringLatin1.newString(value, start, end - start);
}
return StringUTF16.newString(value, start, end - start);
}
// 用val在[index, index+len)范围内的byte值创建String
public static String newString(byte[] val, int index, int len) {
return new String(Arrays.copyOfRange(val, index, index + len), LATIN1);
}
不难,本质上是字节数组拷贝出一个新的字符子串返回。
StringBuilder
public final class StringBuilder
extends AbstractStringBuilder
implements Serializable, Comparable<StringBuilder>, CharSequence {
适用场景:单线程下操作大量字符,非线程安全
核心的append,insert,delete方法都是重写了父类ASB的方法,只是函数签名(参数类型不同)没啥好说的
建议比较下其与StringBuffer的实现
StringBuffer
public final class StringBuffer extends AbstractStringBuilder implements Serializable, Comparable<StringBuffer>, CharSequence {
/**
* A cache of the last value returned by toString. Cleared whenever the StringBuffer is modified.
*/
// 调用toString()后生成的缓存,用于存储ASB中的字符序列。每次更改ASB都会清理缓存
private transient String toStringCache;
第一个与StringBuilder不同的地方就在于多了个transient修饰的toStringCache成员,每次更改ASB之前都会先把这个cache设为null。
那么调用toString的时候就没必要再进行字节数组拷贝构建新字符串了!直接返回这个缓存就好
至于怎么实现线程安全,很简单,所有方法都用synchronized修饰,这也意味着其性能是远远比StringBuilder要低的。当然,如果频繁进行StringBuffer的append操作,JVM开启了逃逸分析功能的话,是可以触发锁消除的,使得性能大大提升。