头像

带翅膀的猫

时光荏苒,我们一直都在

《JDK源码之StringBuilder》

 2月前  •   JDK源码阅读  •     •   39  •   0

StringBuilder

StringBuilder 是一个可变的字符序列。它继承于AbstractStringBuilder,实现了CharSequence接口。StringBuffer 也是继承于AbstractStringBuilder的子类;但是,StringBuilder和StringBuffer不同,前者是非线程安全的,后者是线程安全的。

StringBuilder 和 CharSequence之间的关系图如下:

StringBuilder属性

JAVAchar[] value;
int count;

可以发现StringBuilder的底层也是使用字符数组实现的。count用于表示字符数组中使用的字符个数。

StringBuilder构造方法

JAVApublic StringBuilder() {
    super(16);//初始化char[]大小为16
}
父类方法:
AbstractStringBuilder(int capacity) {
     value = new char[capacity];
}

可以发现我们使用无参构造方法创建StringBuilder时默认初始化大小为16。

JAVApublic StringBuilder(int capacity) {
     super(capacity);
}

我们也可以通过设置capacity自定义初始化大小。

JAVApublic StringBuilder(String str) {
     super(str.length() + 16);
     append(str);
}

使用字符串创建StringBuilder,需要注意的是创建的StringBuilder大小是str,length()+16。

测试StringBuilder strb = new StringBuilder("hello");
System.out.println(strb.capacity());//21

append()方法

JAVA@Override
public StringBuilder append(Object obj) {
     return append(String.valueOf(obj));
}

public static String valueOf(Object obj) {
     return (obj == null) ? "null" : obj.toString();
}

最终调用的都是append(String str)方法:

JAVA@Override
public StringBuilder append(String str) {
	super.append(str);
	return this;
}

public AbstractStringBuilder append(String str) {
	if (str == null)
		return appendNull();
	int len = str.length();
	ensureCapacityInternal(count + len);//确认容量
	str.getChars(0, len, value, count);
	count += len;
	return this;
}

这里需要注意的是,如果传入的字符串为null,那么会调用appendNull()方法,这个方法底层会添加’n’,’u’,’l’,’l’四个字符。

测试StringBuilder strb = new StringBuilder("hello");
strb.append("world");
strb.append(new A(100));
String str2 = null;
strb.append(str2);
System.out.println(strb);//helloworldi=100null

ensureCapacityInternal(count + len)方法

在调用append方法时总会使用此方法确认容量,它传入当前StringBuilder中已使用的容量(count)
和待添加的String的长度(len)之和,这是StringBuilder所需要的最小容量。

JAVAprivate void ensureCapacityInternal(int minimumCapacity) {
	// overflow-conscious code
	if (minimumCapacity - value.length > 0) {
		value = Arrays.copyOf(value,
				newCapacity(minimumCapacity));
	}
}

value.length表示的是当前StringBuilder中字符数组全部容量。如果添加字符串后所需容量大于当前StringBuilder的字符数组容量,那么就需要新开辟空间。

JAVAprivate int newCapacity(int minCapacity) {
	// overflow-conscious code
	int newCapacity = (value.length << 1) + 2;//初步设置,newCapacity为原来空间的2倍+2(这可能会导致溢出)
	if (newCapacity - minCapacity < 0) {
		newCapacity = minCapacity;//如果还是不够大,直接将newCapacity设置为count + len
	}
	//newCapacity <= 0说明溢出了
	//MAX_ARRAY_SIZE - newCapacity < 0说明申请的空间大于设定的最大array大小(Integer.MAX_VALUE-8)
	//如果以上两个满足一个则去执行hugeCapacity()方法
	//如果minCapacity>Integer.MAX_VALUE则抛出OutOfMemoryError异常
	//如果MIN_ARRAY_SIZE<minCapacity则设置为minCapacity,否则设置为MAX_ARRAY_SIZE
	return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
		? hugeCapacity(minCapacity)
		: newCapacity;
}

处理可以往StringBuilder中添加String和Object,还可以添加许多类型的数据,注意添加布尔类型时是添加’t’,’r’,’u’,’e’和’f’,’a’,’l’,’s’,’e’字符。

同时支持添加字符数组中的一部分内容。

JAVA@Override
public StringBuilder append(char[] str, int offset, int len) {
     super.append(str, offset, len);
     return this;
}

delete()方法

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

有了添加,必然少不了删除。我们可以删除StringBuilder中的部分字符,我们可以发现底层就是讲end后的字符全部拷贝到了start后而已。

replace(int start,int end,String str)方法

JAVApublic 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();
	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;
}

可以使用replace方法对StringBuilder中的部分进行替换,并且替换字符串的长度可以大于替换部位的长度。底层依旧使用数组拷贝。

其他大部分方法都是大同小异或者使用了String类的方法,感兴趣可以自行查看。

注意length()和capacity()

调用length()方法返回的是字符数组中字符的数目,调用capacity()方法返回的是字符数组容量,与多少字符无关。

上一篇:
下一篇:

 评论


 已有0条评论

    还没有任何评论,你来说两句吧!