目錄
StringBuilder是什么
核心特性:
StringBuilder數據結構
1. 核心存儲結構(基于父類?AbstractStringBuilder)
2. 類定義與繼承關系
3. 數據結構的核心特點
StringBuilder數據結構的初始化方式
1. 無參構造:默認初始容量為 16
2. 指定初始容量:自定義數組大小
3. 基于已有字符串初始化:容量 = 字符串長度 + 16
初始化的核心邏輯
在append()追加時,如果容量不足,如何擴容
1. 計算所需的最小容量
2. 判斷是否需要擴容
3. 確定新的容量
4. 創建新數組并復制數據
5. 更新相關屬性
源碼解析如下:
深入理解 String 類的不可變性及其內部數據結構_Java字符串拼接性能優化-CSDN博客
小編在這篇博客中講過,當我們對一個String
對象進行修改(如拼接、替換等)時,原對象的內容不會被改變,JVM 會創建一個全新的String
對象來存儲修改后的結果,并將變量引用指向新對象。
String類型的不可變保障線程安全,優化哈希性能,支持常量池復用以及確保數據安全,但在需要頻繁修改字符串的場景中(例如循環拼接大量數據),會產生大量臨時對象,不僅占用額外內存,還會增加垃圾回收的負擔,導致性能顯著下降。而StringBuilder的出現彌補了String類這一缺點,今天,我們就來深入解讀StringBuilder
類的數據結構與擴容方式,看看它是如何高效應對字符串的動態變化的。
StringBuilder是什么
StringBuilder
?是 Java 中用于處理可變字符序列的類,主要用于高效地進行字符串的動態構建和修改,是為解決?String
?類在頻繁字符串操作時的性能問題而設計的。
核心特性:
-
可變性
這是?StringBuilder
?最核心的特性。與?String
?的不可變性不同,StringBuilder
?的內部字符序列可以直接修改(如拼接、插入、刪除等),所有操作都在原有對象上進行,不會像?String
?那樣每次修改都創建新對象,從而大幅減少內存消耗和垃圾回收壓力。 -
高效的字符串操作
提供了豐富的操作方法,如?append()
(追加任意類型數據)、insert()
(指定位置插入)、delete()
(刪除區間字符)、replace()
(替換字符)等,這些方法均針對性能優化設計,執行效率遠高于?String
?的同類操作(尤其在循環拼接等場景中)。 -
非線程安全
StringBuilder
?的方法沒有添加?synchronized
?同步鎖,因此在多線程環境下并發修改可能導致數據不一致。但這也使其在單線程場景中性能優于線程安全的?StringBuffer
(避免了同步帶來的開銷)。 -
動態擴容機制
內部基于字符數組(Java 9 后優化為字節數組,根據字符編碼動態調整存儲方式)存儲數據,初始容量可自定義(默認 16)。當追加內容導致容量不足時,會自動擴容(通常為原容量的 2 倍加 2),并復制原有數據到新數組,平衡了內存占用和操作效率。 -
與 String 的便捷轉換
可通過?toString()
?方法快速轉換為?String
?對象,滿足需要?String
?類型參數的場景,轉換過程會創建新的?String
?對象,但僅執行一次,性能開銷可控。
// 創建對象
StringBuilder sb = new StringBuilder();// 追加內容
sb.append("Hello");
sb.append(" ");
sb.append("World");// 插入內容
sb.insert(5, ","); // 在索引5處插入逗號// 轉換為String
String result = sb.toString(); // 結果:"Hello, World"
StringBuilder數據結構
1. 核心存儲結構(基于父類?AbstractStringBuilder
)
StringBuilder
?本身不直接定義存儲結構,而是繼承自抽象類?AbstractStringBuilder
,核心數據存儲由父類維護,主要包含兩個關鍵成員變量:
-
存儲字符的數組:
- Java 8 及之前:使用?
char[] value
?數組,每個字符固定占用 2 個字節(UTF-16 編碼),直接存儲字符序列。 - Java 9 及之后:優化為?
byte[] value
?數組,根據字符類型動態選擇編碼:- 若字符為 ASCII 或 Latin-1 范圍內(單字節可表示),則用 1 字節存儲,節省內存;
- 若包含多字節字符(如中文、日文),則用 2 字節存儲(UTF-16 編碼)。
- Java 8 及之前:使用?
-
字符長度計數器:
int count
,記錄當前存儲的有效字符數量(而非數組容量)。
2. 類定義與繼承關系
StringBuilder
?的類定義簡化如下(省略部分接口實現):
// 繼承 AbstractStringBuilder,實現字符序列和可追加接口
public final class StringBuilder extends AbstractStringBuilder implements CharSequence, Appendable {// 構造方法(調用父類構造)public StringBuilder() {super(16); // 默認初始容量 16}public StringBuilder(int capacity) {super(capacity); // 指定初始容量}public StringBuilder(String str) {super(str.length() + 16); // 初始容量 = 字符串長度 + 16append(str);}// 其他方法(如 append、insert 等,實際調用父類方法)
}// 父類 AbstractStringBuilder 的核心定義
abstract class AbstractStringBuilder implements Appendable, CharSequence {// Java 9+ 用 byte[],Java 8 及之前用 char[]byte[] value; int count; // 有效字符數// 父類構造方法(初始化數組容量)AbstractStringBuilder(int capacity) {if (capacity < 0)throw new NegativeArraySizeException();value = new byte[capacity]; // 初始化數組}
}
3. 數據結構的核心特點
- 數組存儲:通過連續的數組結構存儲字符,支持快速隨機訪問(如通過索引獲取字符)。
- 容量與長度分離:
value
?數組的長度是「容量」(可容納的最大字符數),count
?是「實際長度」(已存儲的字符數),容量 ≥ 長度。 - 動態適配:Java 9+ 的?
byte[]
?優化減少了單字節字符的內存占用,平衡了存儲效率和兼容性。
StringBuilder
數據結構的初始化方式
StringBuilder
?內部數據結構(字符數組)的初始化方式由其構造方法決定,主要通過以下 3 種方式指定初始容量,以適配不同場景的字符串操作需求:
1. 無參構造:默認初始容量為 16
當使用無參構造方法?new StringBuilder()
?時,會調用父類?AbstractStringBuilder
?的構造方法,初始化一個容量為 16 的字符數組(Java 8 及之前為?char[]
,Java 9+ 為?byte[]
)。
StringBuilder sb = new StringBuilder();
// 內部數組初始容量為 16,可直接存儲 16 個字符(或更多,取決于編碼)
2. 指定初始容量:自定義數組大小
如果已知字符串的大致長度,可通過?new StringBuilder(int capacity)
?手動指定初始容量,避免后續頻繁擴容。父類會根據傳入的?capacity
?初始化對應大小的數組。
StringBuilder sb = new StringBuilder(100);
// 內部數組初始容量為 100,適合預計存儲約 100 個字符的場景
3. 基于已有字符串初始化:容量 = 字符串長度 + 16
當傳入一個?String
?對象作為參數(new StringBuilder(String str)
)時,初始容量會設為「字符串長度 + 16」,既容納原有字符串,又預留 16 個字符的空間供后續追加。同時會將傳入的字符串內容復制到內部數組中。
String str = "hello"; // 長度為 5
StringBuilder sb = new StringBuilder(str);
// 內部數組初始容量 = 5 + 16 = 21,且已存儲 "hello" 這 5 個字符
初始化的核心邏輯
無論哪種方式,最終都是通過父類?AbstractStringBuilder
?的構造方法完成數組初始化:
// 父類 AbstractStringBuilder 的構造方法(簡化)
AbstractStringBuilder(int capacity) {if (capacity < 0) {throw new NegativeArraySizeException();}// 初始化數組(Java 8 為 char[],Java 9+ 為 byte[])value = new byte[capacity]; // 以 Java 9+ 為例
}
通過合理選擇初始化方式(尤其是指定初始容量),可以減少后續?append()
?操作時的擴容次數,進一步提升?StringBuilder
?的性能。這種結構設計使得?StringBuilder
?既能直接修改原有數據(實現可變性),又能通過數組的連續內存特性保證操作效率,是其高性能的核心基礎。
在append()追加時,如果容量不足,如何擴容
當使用?StringBuilder
?的?append()
?方法追加字符或字符串時,如果當前內部字符數組的剩余容量不足以容納新的數據,就會觸發擴容操作。StringBuilder
?擴容的具體流程如下(以 Java 8 及之后版本為例,Java 9 后對存儲方式有優化,但擴容邏輯主體一致 ):
1. 計算所需的最小容量
在追加數據之前,StringBuilder
?會先計算追加新數據后總共需要的字符數量,將其記為?minCapacity
?。這個值是在原有有效字符數量(由?count
?變量記錄)的基礎上,加上即將追加的數據的字符長度。
2. 判斷是否需要擴容
將計算得到的?minCapacity
?與當前字符數組(value
)的容量(即?value.length
?)進行比較。如果?minCapacity
?大于當前數組的容量,就意味著當前數組無法容納新追加的數據,此時需要進行擴容操作。
3. 確定新的容量
StringBuilder
?采用一種簡單的擴容策略來確定新的數組容量,具體的計算方式是將當前容量乘以 2 再加 2 ,即
newCapacity = oldCapacity * 2 + 2
例如,如果當前數組的容量?oldCapacity
?為 16,那么擴容后的新容量?newCapacity
?就是?16 * 2 + 2 = 34
?。
不過,如果通過上述計算得到的?newCapacity
?仍然小于?minCapacity
?,則會直接將?newCapacity
?設置為?minCapacity
?,以確保新數組能夠容納追加后的數據。
4. 創建新數組并復制數據
確定了新的容量?newCapacity
?后,StringBuilder
?會創建一個新的字符數組(char[]
?),其長度為?newCapacity
?。然后,通過系統調用(System.arraycopy()
?)將原數組中的數據復制到新數組中。這一步保證了原有的字符序列不會丟失,并且新數組有足夠的空間來存儲即將追加的數據。
5. 更新相關屬性
復制完成后,StringBuilder
?會將內部用于存儲字符的數組引用(value
?)指向新創建的數組,同時更新其他可能相關的屬性(例如在一些優化的實現中,可能會涉及到對編碼標識等屬性的調整 ),使得后續的追加操作可以在新的、更大容量的數組上進行。
源碼解析如下:
abstract class AbstractStringBuilder implements Appendable, CharSequence {// 追加字符的方法public AbstractStringBuilder append(char c) {ensureCapacityInternal(count + 1);putChar(count++, c);return this;}// 追加字符串的方法public AbstractStringBuilder append(String str) {if (str == null)return appendNull();int len = str.length();ensureCapacityInternal(count + len);str.getBytes(coder, 0, len, value, count);count += len;return this;}// 確保內部容量足夠的方法private void ensureCapacityInternal(int minimumCapacity) {// overflow-conscious codeif (minimumCapacity - value.length > 0)expandCapacity(minimumCapacity);}// 執行擴容的方法void expandCapacity(int minimumCapacity) {int newCapacity = value.length * 2 + 2;if (newCapacity - minimumCapacity < 0)newCapacity = minimumCapacity;if (newCapacity < 0) {if (minimumCapacity < 0) // overflowthrow new OutOfMemoryError();newCapacity = Integer.MAX_VALUE;}value = Arrays.copyOf(value, newCapacity);}
}
append(char c)
?和?append(String str)
?方法在追加數據前,都會先調用?ensureCapacityInternal(int minimumCapacity)
?方法,計算追加數據后所需的最小容量minimumCapacity
,并判斷當前數組容量是否足夠。- 如果當前容量不足,
ensureCapacityInternal
?方法會調用?expandCapacity(int minimumCapacity)
?進行擴容。expandCapacity
?方法先按照?newCapacity = oldCapacity * 2 + 2
?的規則計算新容量newCapacity
,如果計算出的新容量小于minimumCapacity
,則將newCapacity
設置為minimumCapacity
?。同時,還會處理新容量溢出的情況,若溢出且minimumCapacity
小于 0,拋出OutOfMemoryError
,否則將newCapacity
設置為Integer.MAX_VALUE
?。最后通過Arrays.copyOf
方法將原數組數據復制到新的、更大容量的數組中,完成擴容。
通過以上步驟,StringBuilder
?實現了在容量不足時的自動擴容,從而保證了在頻繁追加數據的情況下,仍然能夠高效地進行字符串操作。
StringBuilder
憑借其可變的字符序列設計、靈活的初始化方式以及智能的擴容機制,完美解決了String
在頻繁字符串操作時的性能瓶頸。從數據結構到擴容邏輯,每一處都為高效處理動態字符串而生,是 Java 中構建復雜字符串、提升程序性能的得力工具,尤其在大量字符串拼接等場景下,能顯著優化內存使用與執行效率。
有問題歡迎留言!!!😗
肥嘟嘟左衛門就講到這里啦,記得一鍵三連!!!😗