深入理解String,StringBuilder,StringBuffer
首先明確什麼是不可變字元序列? 我們都知道在Java中, String類是不可變的。那麼到底什麼是不可變的物件呢? 可以這樣理解:如果一個物件建立完成之後,不能再改變它的狀態,那麼該物件就是不可變的。不能改變狀態的意思是,不能改變物件內的屬性,包括基本資料型別的值不能改變,引用型別指向的物件的狀態也不能改變。或者這樣理解就是物件狀態不可變,而引用是可再次指向其他物件的。 首先說明一點不是說String類被final修飾就是不可變的,而是成員變數(域)被final修飾 原始碼中是這樣體現的 /** The value is used for character storage. */ private final char value[]; 具體看如下程式碼執行結果(都是針對同一物件進行修改操作)
public class Demo01 { public static void main(String[] args) { String s1=new String("abcdef"); System.out.println(Integer.toHexString(s1.hashCode())); System.out.println(s1); s1.substring(3, 5); System.out.println(Integer.toHexString(s1.hashCode())); System.out.println(s1); } }
記憶體地址為ab199863 abcdef 記憶體地址為ab199863 abcdef 對同一物件進行修改操作沒有發生改變即為不可變 為什麼StringBuilder與StringBuffer是可變的? 原始碼中是這樣體現的(摘自父類AbstractStringBuilder) /** * The value is used for character storage. */ char[] value;
public class Demo02 { > public static void main(String[] args) { > StringBuilder sb=new StringBuilder("abcdef"); > > System.out.println(Integer.toHexString(sb.hashCode())); > System.out.println(sb); > > sb.setCharAt(0, 'A'); > > System.out.println(Integer.toHexString(sb.hashCode())); > System.out.println(sb); > } > }
記憶體地址為15db9742 abcdef 記憶體地址為15db9742 Abcdef 對同一物件進行修改操作發生改變即為可變
接下來看三者間的效率 我們對5萬個數進行字串拼接效率如下 如圖可得,三者間的效率差距有多大 StringBuilder與StringBuffer的區別 StringBuilder:執行緒不安全,效率高 StringBuffer:執行緒安全:效率低
摘自JDK1.8 StringBuffer的append部分過載方法原始碼
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
由於append方法加了同步拼接時導致效率低 return this可以進行鏈式呼叫
根據effective java第51條規定: 通過字串連線符進行操作時,時間複雜度為O(n2),這是由於字串不可變而導致的結果,當兩個字串被連線在一起時,他們的內容都要被拷貝,相反,而應該使用StringBuilder的append方法,時間是線性的,另一種方法是使用字元陣列,或者每次只處理一個字串
可變即可擴容
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
擴容實際上就是陣列的拷貝