字符串拼接源码分析
String
- 方式一:“+”运算符
String a = "qsy";
String b = a + "isA"; //a = "qsy" ; b = "qsyisA"
- 方式二:concat方法
String a = "qsy";
String b = a.concat("isA"); //a = "qsy" ; b = "qsyisA"
“+”运算符和concat()方法无法在原字符串基础上进行更改,需生成新的字符串。
以下为 String.java 部分源码:
/** The value is used for character storage. */
private final char value[];
public String() {
this.value = "".value;
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
可以知道String内部是使用字符数组 char value[]
储存,concat()方法每次调用生成一个新的 char buf[]
和String
对象。
StringBuilder
StringBuilder a = new StringBuilder("qsy");
StringBuilder b = a.append("isA"); //a = "qsyisA" ; b = "qsyisA"
append()方法可以直接在原字符串基础上进行更改。
以下为 StringBuilder 部分源码:
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{
...
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
...
}
append()方法在其父类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;
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;
}
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;
}
...
}
ensureCapacityInternal()方法是给数组 char[] value
重新分配足够的空间并将原value中的内容复制过去。
append()方法在调用ensureCapacityInternal()之后使用String.getChar()
方法将str中的内容添加在value中。
其中,getChar()方法源码如下:
//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.java
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
System类的arraycopy使用native关键字,底层调用了C/C++的数组复制方法。
StringBuffer类
//StringBuffer.java
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
//AbstractStringBuilder.java
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;
}
源码其实与StringBuilder相差无几,都继承 AbstractStringBuilder 类,但在每个方法前加了synchronized
关键字用来保证线程安全。还有一个toStringCache
字段作为缓存,缓存在每次该对象被修改时清空,每次调用toString()方法时会重新更新缓存,便于在对象没有修改时直接输出,稍微平衡了一下加入线程安全机制损失的性能。
总结
其实三个类拼接时都是通过先申请一个新的大小char数组,再将两个字符串内部的char数组往新数组里复制得到。只是StringBuilder、StringBuffer在得到结果后赋值给自己,而String则只是通过函数的return返回。