標籤:stat abstract 儲存 ring for 資料 final 字元數組 用法
Java裡面提供了String,StringBuffer和StringBuilder三個類來封裝字串
0. 不當用法
String result = ""; for (String s : hugeArray) { result = result + s; }
不要使用String類的"+"來進行頻繁的拼接,因為那樣的效能極差的,應該使用StringBuffer或StringBuilder類,這在Java的最佳化上是一條比較重要的原則
1. String類封裝的字串是不可變的
字串是由若干個字元線性排列組成的,String類的關鍵源碼如下
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[];//final類型char數組//省略其他代碼…………}
因為有“final”修飾符,所以可以String類封裝的字串是不可變的。那麼增刪改是怎麼實現的呢?下面是字串截取的關鍵源碼
public String substring(int beginIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } int subLen = value.length - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } //當對原來的字串進行截取的時候(beginIndex >0),返回的結果是建立的對象 return (beginIndex == 0) ? this : new String(value, beginIndex, subLen); }
可以看到截取源碼是產生了一個新的String對象返回的。
所以,在對String類型的字串進行大量“插入”和“刪除”操作時會產生大量的臨時變數。
既然String類封裝的字串是不可變的,那麼是否應該有一個類來封裝可變數組。答案是有的,StringBuilder與StringBuffer封裝類都是可變的。
2. 如何做到封裝數組可變
StringBuilder與StringBuffer都繼承自AbstractStringBuilder抽象類別,在AbstractStringBuilder中也是使用字元數組儲存字串,其關鍵代碼如下
abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ char[] value;//一個char類型的數組,非final類型,這一點與String類不同 /** * 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];//構建了長度為capacity大小的數組 }//其他代碼省略…………}
StringBuffer類實現代碼如下
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence{ /** * Constructs a string buffer with no characters in it and an * initial capacity of 16 characters. */ public StringBuffer() { super(16);//建立一個預設大小為16的char型數組 } /** * Constructs a string buffer with no characters in it and * the specified initial capacity. * * @param capacity the initial capacity. * @exception NegativeArraySizeException if the {@code capacity} * argument is less than {@code 0}. */ public StringBuffer(int capacity) { super(capacity);//自訂建立大小為capacity的char型數組 }//省略其他代碼…………
可以看到StringBuffer建立字串對象預設的大小是16,當然也可以傳入數組大小。
下面列下StringBuffer的用法
public class Test{ public static void main(String args[]){ StringBuffer sBuffer = new StringBuffer("hello"); sBuffer.append(" "); sBuffer.append("world"); System.out.println(sBuffer); }}
輸出
hello world
下面看源碼分析下append函數,來看下如何做到長度可變
public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); //調用下面的ensureCapacityInternal方法 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) //調用下面的expandCapacity方法實現“擴容”特性 expandCapacity(minimumCapacity); } /** * This implements the expansion semantics of ensureCapacity with no * size check or synchronization. */ void expandCapacity(int minimumCapacity) { //“擴充”的數組長度是按“擴充”前數組長度的2倍再加上2 byte的規則來擴充 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返回的新的char[]對象,從而達到“擴容”的特性 value = Arrays.copyOf(value, newCapacity); }
可以看到空間擴充室友copyOf函數實現:
public static char[] copyOf(char[] original, int newLength) { //建立長度為newLength的char數組,也就是“擴容”後的char 數組,並作為傳回值 char[] copy = new char[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy;//返回“擴容”後的陣列變數 }3. StringBuilder與StringBuffer 區別
AbstractStringBuilder是StringBuilder與StringBuffer的公用父類,定義了一些字串的基本操作,如expandCapacity、append、insert、indexOf等公用方法,StringBuilder和StringBuffer的方法實現基本上一致,不同的是StringBuffer類的方法前多了個synchronized關鍵字,即StringBuffer是安全執行緒的。
public synchronized StringBuffer reverse() { super.reverse(); return this;}public int indexOf(String str) { return indexOf(str, 0); //存在 public synchronized int indexOf(String str, int fromIndex) 方法}
(1)append,insert,delete方法最根本上都是調用System.arraycopy()這個方法來達到目的
(2)substring(int, int)方法是通過重新new String(value, start, end - start)的方式來達到目的。因此,在執行substring操作時,StringBuilder和String基本上沒什麼區別。
4. 使用情境
如果是多線程環境下涉及到共用變數的插入和刪除操作,StringBuffer則是首選。如果是非多線程操作並且有大量的字串拼接,插入,刪除操作則StringBuilder是首選。畢竟String類是通過建立臨時變數來實現字串拼接的,耗記憶體還效率不高,怎麼說StringBuilder是通過JNI方式實現終極操作的。
5. 總結
- String類型的字串對象是不可變的,一旦String對象建立後,包含在這個對象中的字元系列是不可以改變的,直到這個對象被銷毀。
- StringBuilder和StringBuffer類型的字串是可變的,不同的是StringBuffer類型的是安全執行緒的,而StringBuilder不是安全執行緒的
- 如果是多線程環境下涉及到共用變數的插入和刪除操作,StringBuffer則是首選。如果是非多線程操作並且有大量的字串拼接,插入,刪除操作則StringBuilder是首選。
- 如果要操作少量的資料,用String;單線程操作大量資料,用StringBuilder;多線程操作大量資料,用StringBuffer。
java中String、StringBuffer、StringBuilder的區別