源代碼是萬物之源率寡。——黑客帝國
先看下String的源代碼:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];//char數(shù)組也就是String的值
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
}
所以其實String就是一個char[]疏虫。唯一需要注意的點應(yīng)該就是final修飾符坯临,也就意味著value是常量不可更改老速。這個和之前object-c中的設(shè)計很像,但不知道在這里是不是為了線程安全磺樱。從后面replace等函數(shù)的實現(xiàn)也可以看出,更改String的值需要重新申請一個String變量。
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
/* avoid getfield opcode */
關(guān)于這個注釋,查了一下。得到的答案是將變量復(fù)制到局部變量中矩父,這樣在下面的循環(huán)操作中就可以不用反復(fù)的從堆中取數(shù)據(jù)了。(棧中的訪問速度更快蝗茁,那是不是操作次數(shù)比較多的變量都可以用這種方法去增加速度呢署驻?)
但是在這個文件夾中我沒有找到+號的重載函數(shù)(當(dāng)然java本身不允許重載運算符),經(jīng)過百度發(fā)現(xiàn)這個加號的重載是在編譯階段實現(xiàn)的。
//以下兩者是等價的
s = i + ""
s = String.valueOf(i);
//以下兩者也是等價的
= "abc" + i;
= new StringBuilder("abc").append(i).toString();
再看下StringBuilder的源代碼:
/**
* 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() {
}
可變的char[]乖订,以及長度。
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
這里對于為什么要在初始化的時候預(yù)留一個16個大小的數(shù)組還不太明白。
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;
}
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);
}
這里就不貼所有的代碼了攀芯,主要就是如果是對象則會調(diào)用string的valueof()轉(zhuǎn)成String,如果是String就是調(diào)用getchars()文虏,再對char[]添加賦值侣诺。
所以其性能差異即在少做了一步string和StringBuilder的轉(zhuǎn)化。
而String s = "abc";這樣的操作會在常量字符區(qū)生成一個"abc"常量氧秘,也會增加開銷年鸳。
java,StringBuilder預(yù)留16位
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);//在添加String時確定內(nèi)部空間足夠
str.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)//如果超過了預(yù)留的空間大小丸相,則選擇擴容搔确。
expandCapacity(minimumCapacity);
}
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;//將其擴充為2倍的大小加2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;//如果還不夠大小灭忠,則將空間擴充為兩個字符串大小之和
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;//如果字符串之和小于int最大值膳算,但是兩倍太大會導(dǎo)致overflow,則將其設(shè)置為int最大值
}
value = Arrays.copyOf(value, newCapacity);
}
這樣做最大的好處應(yīng)該是在擴展小的字符串時不用每次都申請空間弛作,只有在原有空間已滿的時候再進行擴容涕蜂,典型的空間換時間。