我们知道,在java中,如果要对于一个字符串进行操作,比如增删操作,如果直接用+
来进行的话,效率是比较低的。作为替代,我们一般使用StringBuilder
来进行这样的操作。
所以,这一次,我们就来研究一下StringBuilder的源码
构造函数
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/** use serialVersionUID for interoperability */
static final long serialVersionUID = 4383685877147921099L;
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
/**
* Constructs a string builder with no characters in it and an
* initial capacity specified by the {@code capacity} argument.
*
* @param capacity the initial capacity.
* @throws NegativeArraySizeException if the {@code capacity}
* argument is less than {@code 0}.
*/
public StringBuilder(int capacity) {
super(capacity);
}
/**
* Constructs a string builder initialized to the contents of the
* specified string. The initial capacity of the string builder is
* {@code 16} plus the length of the string argument.
*
* @param str the initial contents of the buffer.
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
/**
* Constructs a string builder that contains the same characters
* as the specified {@code CharSequence}. The initial capacity of
* the string builder is {@code 16} plus the length of the
* {@code CharSequence} argument.
*
* @param seq the sequence to copy.
*/
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
...
}
可以看到,当我们不指定初始的容量的时候,会调用父类AbstractStringBuilder
的构造函数,并默认初始容量为16。
所以,我们来看看’AbstractStringBuilder’的构造方法:
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
...
}
其中,主要关注:
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
可以看到,在这里根据设定的初始容量,初始化出了一个相应长度的char型数组value
。
append方法
这里以str的append
方法为例,看一下StringBuilder的append方法是怎样工作的。
首先是StringBuilder
中的append(String str)
方法。
public StringBuilder append(String str) {
super.append(str);
return this;
}
指向了父类中的append
方法,那么就来看看父类中的定义:
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;
}
其中的ensureCapacityInternal
是为了保证容量能够装得下这个输入的str。
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
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;
}
这里的扩容方式为先将容量乘2再加2,再与所需容量进行比较,若小于所需容量,则取所需容量,后面还有防止溢出的操作,写得非常严谨,这里值得我们学习与借鉴。获得更新后的容量后,新建一个新的容量的数组,并将之前的数据用Arrays.copyOf()
方法复制进去,并更新为新的成员变量value
。
我们回到append
方法中来,在进行完ensureCapacityInternal
后,就要将所需添加的数据加进来了,这里调用的是String
的getChars
方法。
# String.java
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
这里调用System.arraycopy
来将String
内部的成员变量char型数组value
(注意这里是String的,也就是append的参数里的那个变量)从0到str.length(),也就是所有成员,复制到dst中,也就是StringBuilder
的成员变量value
中。之后,便完成了整个append
的操作。