淺談String、StringBuffer 、StringBuilder和StringJoiner
可變性
String 類中使用 final 關(guān)鍵字修飾字符數(shù)組來保存字符串,private final char value[],所以 String 對象是不可變的。而StringBuilder 與 StringBuffer 都繼承自 AbstractStringBuilder 類,在 AbstractStringBuilder 中使用字符數(shù)組保存字符串char[] value, 沒有用 final 關(guān)鍵字修飾,所以這兩種對象都是可變的。
安全性
String 中的對象是不可變的,線程安全。
StringBuffer 對方法加了同步鎖或者對調(diào)用的方法加 了同步鎖,所以是線程安全的。
下面是StringBuffer 源碼中的部分代碼:
@Override
public synchronized int length() {
return count;
}
@Override
public synchronized int capacity() {
return value.length;
}
@Override
public synchronized void ensureCapacity(int minimumCapacity) {
super.ensureCapacity(minimumCapacity);
}
StringBuilder 可變,但是沒有對方法加同步鎖,所以是非線程安全的。
性能
每次對 String 類型進(jìn)行改變的時候,都會生成一個新的 String 對象,然后將指針指向新的 String 對象。StringBuffer 與StringBuilder 每次都是對對象本身進(jìn)行操作,而不是生成新的對象并改變對象引用。相同情況下使用 StringBuilder 相比使用 StringBuffer 能獲得 10%~15% 左右的性能提升,但卻有多線程不安全的風(fēng)險。
對于三者使用的總結(jié)
操作少量的數(shù)據(jù): 適用String
單線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù): 適用StringBuilder
多線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù): 適用StringBuffer
AbstractStringBuilder 擴(kuò)容機(jī)制
由于StringBuilder 與 StringBuffer 都繼承自 AbstractStringBuilder 類,所以就來簡單的探討一下StringBuilder的擴(kuò)容機(jī)制,StringBuffer 只需在此基礎(chǔ)上加上同步鎖就是。
下面我們在源碼中分析:
public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
從上面StringBuilder的構(gòu)造方法可以看出默認(rèn)容量為16,可以傳入?yún)?shù)(CharSequence為可讀可寫序列,意思還可以傳一個StringBuilder等的變量)。
/*
AbstractStringBuilder 中成員變量
*/
char[] value;//StringBuilder本質(zhì)就是可變長度的字符數(shù)組
int count;//已有內(nèi)容的長度
/*
這個方法保證minimumCapacity > 0
*/
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0)
ensureCapacityInternal(minimumCapacity);
}
/*
這個方法就是獲得一個長度較大的字符數(shù)組
*/
private void ensureCapacityInternal(int minimumCapacity) {
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
/*
這個方法就是獲取value新的值max(value.length*2+2,minimumCapacity)
*/
private int newCapacity(int minCapacity) {
int newCapacity = (value.length << 1) + 2;//左移一位,擴(kuò)大兩倍
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
/*
獲取容量的最大值,防止內(nèi)存溢出
*/
private int hugeCapacity(int minCapacity) {
if (Integer.MAX_VALUE - minCapacity < 0) { // 內(nèi)存溢出
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE)
? minCapacity : MAX_ARRAY_SIZE;
}
對于大數(shù)據(jù)的2進(jìn)制運(yùn)算,位移運(yùn)算符比那些普通運(yùn)算符的運(yùn)算要快很多,因?yàn)槌绦騼H僅移動一下而已,不去計算,這樣提高了效率,節(jié)省了資源
這里簡單說說StringJoiner
StringJoiner(JDK1.8),在源碼中可以看到,內(nèi)部其實(shí)運(yùn)用的是StringBuilder ,感覺就是在StringBuilder 的基礎(chǔ)上可以快速拼接字符串。
public final class StringJoiner {
private final String prefix;//開頭使用的字符序列
private final String delimiter;//要添加到每個元素之間的間隔符
private final String suffix;// 最后使用的字符序列
private StringBuilder value;//內(nèi)部運(yùn)用StringBuilder
}
/*
添加內(nèi)容是調(diào)用StringBuilder的append()添加方法
*/
public StringJoiner add(CharSequence newElement) {
prepareBuilder().append(newElement);
return this;
}
/*
返回一個StringBuilder的值
*/
private StringBuilder prepareBuilder() {
if (value != null) {
value.append(delimiter);
} else {
value = new StringBuilder().append(prefix);//若為null就new StringBuilder()
}
return value;
}