StringBuilder類的數據結構和擴容方式解讀

目錄

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?類在頻繁字符串操作時的性能問題而設計的。

核心特性:

  1. 可變性
    這是?StringBuilder?最核心的特性。與?String?的不可變性不同,StringBuilder?的內部字符序列可以直接修改(如拼接、插入、刪除等),所有操作都在原有對象上進行,不會像?String?那樣每次修改都創建新對象,從而大幅減少內存消耗和垃圾回收壓力。

  2. 高效的字符串操作
    提供了豐富的操作方法,如?append()(追加任意類型數據)、insert()(指定位置插入)、delete()(刪除區間字符)、replace()(替換字符)等,這些方法均針對性能優化設計,執行效率遠高于?String?的同類操作(尤其在循環拼接等場景中)。

  3. 非線程安全
    StringBuilder?的方法沒有添加?synchronized?同步鎖,因此在多線程環境下并發修改可能導致數據不一致。但這也使其在單線程場景中性能優于線程安全的?StringBuffer(避免了同步帶來的開銷)。

  4. 動態擴容機制
    內部基于字符數組(Java 9 后優化為字節數組,根據字符編碼動態調整存儲方式)存儲數據,初始容量可自定義(默認 16)。當追加內容導致容量不足時,會自動擴容(通常為原容量的 2 倍加 2),并復制原有數據到新數組,平衡了內存占用和操作效率。

  5. 與 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 編碼)。
  • 字符長度計數器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 中構建復雜字符串、提升程序性能的得力工具,尤其在大量字符串拼接等場景下,能顯著優化內存使用與執行效率。

有問題歡迎留言!!!😗

肥嘟嘟左衛門就講到這里啦,記得一鍵三連!!!😗

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/96116.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/96116.shtml
英文地址,請注明出處:http://en.pswp.cn/web/96116.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LangChain實戰(十七):構建與PDF/PPT文檔對話的AI助手

本文是《LangChain實戰課》系列的第十七篇,將專篇深入講解如何構建能夠與PDF和PPT文檔進行智能對話的AI助手。通過學習本文,您將掌握復雜格式文檔的解析技巧、文本與表格處理技術,以及實現精準問答的系統方法。 前言 在日常工作和學習中,PDF和PPT文檔是我們最常接觸的文檔…

魚眼相機模型

魚眼相機模型 最近涉及魚眼相機模型、標定使用等&#xff0c;作為記錄&#xff0c;更新很久不曾更新的博客。 文章目錄魚眼相機模型1 相機成像2 魚眼模型3 畸變3.1 適用針孔和MEI3.2 Kannala-Brandt魚眼模型4 代碼實現1 相機成像 針孔相機&#xff1a;所有光線從一個孔&#xf…

大語言模型提示詞工程詳盡實戰指南

引言&#xff1a;與大型語言模型&#xff08;LLM&#xff09;高效對話的藝術大型語言模型&#xff08;LLM&#xff09;——例如我們熟知的GPT系列、Claude、Llama等——在自然語言處理&#xff08;NLP&#xff09;領域展現了驚人的能力&#xff0c;能夠執行文本摘要、翻譯、代碼…

HTTP 請求體格式詳解

1. 概覽與概念 Content-Type&#xff1a;HTTP 請求/響應頭&#xff0c;表示消息體的媒體類型&#xff08;MIME type&#xff09;。服務端用它決定如何解析請求體。常見場景&#xff1a; 純結構化數據&#xff08;JSON&#xff09; → application/json表單 文件上傳 → multip…

事務設置和消息分發

事務 RabbitMQ是基于AMQP協議實現的&#xff0c;該協議實現了事務機制&#xff0c;因此RabbitMQ也支持事務機制. SpringAMQP也提供了對事務相關的操作&#xff0c;RabbitMQ事務允許開發者確保消息的發送和接收是原子性的&#xff0c;要么 全部成功&#xff0c;要么全部失敗.| 前…

Python 中 try / except / else / finally 異常處理詳解

1. 基本結構 try:# 可能會拋出異常的代碼 except SomeException as e:# 捕獲并處理異常 else:# 如果 try 中代碼沒有異常&#xff0c;就執行這里 finally:# 無論是否發生異常&#xff0c;最后都會執行這里2. 各部分的作用 try 用途&#xff1a;包含可能發生異常的代碼段。如果代…

冰火島 Tech 傳:Apple Foundation Models 心法解密(下集)

引子 上集說到冰火島冰屋內,謝遜、張翠山、殷素素三人親見 “指令(Instructions)” 如何讓 AI 脫胎換骨,從木訥報地名的 “愣頭青”,變身為文采斐然的 “旅行作家”。 正當素素驚嘆這 AI 武學的奇妙時,謝遜卻突然神色一凜,指著手腕上用冰屑刻的 “4096” 字樣道:“這等…

Qt信號與槽機制全面解析

? 1. 核心概念信號與槽是Qt獨創的一種對象間通信機制&#xff0c;它使得一個對象的狀態變化或事件發生能夠自動通知其他對象作出響應&#xff0c;從而實現高度解耦的代碼設計。1.1 信號&#xff08;Signals&#xff09;定義&#xff1a;信號是由對象在特定事件發生時發出&…

2025年COR SCI2區,基于近似細胞分解的能源高效無人機路徑規劃問題用于地質災害監測,深度解析+性能實測

目錄1.摘要2.問題描述與數學模型3.能源網格混合元啟發式算法4.結果展示5.參考文獻6.代碼獲取7.算法輔導應用定制讀者交流1.摘要 本文提出了一種能源高效的無人機路徑規劃方法&#xff08;EURP&#xff09;用于監測分散的地質災害易發區域&#xff0c;通過建立無人機飛行模式的…

LangChain關于提示詞的幾種寫法

上文中 <---->(可點擊移步查閱),我們已經了解到LangChain如何編寫調用大模型的一個基本框架, 在編寫完這個基本框架后,就要與大模型進行交互了,這時候,就需要我們編寫提示詞了, 那么以下,就會介紹提示詞的幾種編寫方式 1 可攜帶變量的提示詞 1.1 PromptTempla…

CyberPoC 是一個現代化的網絡安全練習和競賽平臺,支持容器化部署的安全挑戰,為用戶提供實踐網絡安全技能的環境。

工具介紹 CyberPoC 是一個現代化的網絡安全練習和競賽平臺&#xff0c;支持容器化部署的安全挑戰&#xff0c;為用戶提供實踐網絡安全技能的環境。 &#x1f680; 快速開始 使用 Docker Compose (推薦) 克隆項目 git clone https://github.com/dushixiang/cyberpoc.gitcd cy…

【單片機day03】

UART串口通用異步收發器&#xff0c;2個串口(1個串口被用于ISP下載程序&#xff0c;1個串口被用于和主機之間的通信)全雙工&#xff08;兩根線TX、RX&#xff09;、串行、異步通信方式&#xff1a;單工&#xff1a;發送方和接收方是固定的&#xff0c;數據只能從發送方 發給 …

react16到react19更新及底層實現是什么以及區別

好問題 &#x1f44d;。React 16 → 19 的更新跨度其實涵蓋了 React 的幾個“時代級”的演進&#xff1a;從 Fiber 架構 的重寫、到 并發特性 的引入&#xff0c;再到 React Server Components 和 現代語法支持。我幫你梳理下主要更新點和底層實現的區別&#xff0c;方便你系統…

【分享】基于百度腦圖,并使用Vue二次開發的用例腦圖編輯器組件

偶然間發現了這個項目&#xff0c;分享出來。 【分享】基于百度腦圖&#xff0c;并使用Vue二次開發的用例腦圖編輯器組件1 項目地址2 項目簡介3 項目部署3.1 安裝node和npm3.2 項目下載3.3 修改npm鏡像源3.4 部署4 項目中使用1 項目地址 基于百度腦圖&#xff0c;并使用Vue二次…

Kotlin中抽象類和開放類

抽象類 (Abstract Class) 定義和特點 抽象類使用 abstract 關鍵字聲明&#xff0c;是一種不能被直接實例化的特殊類&#xff0c;主要用于被其他類繼承。 abstract class Base {open fun f() {} }abstract class Derived : Base() {override abstract fun f() // 抽象成員在類中…

TensorFlow深度學習實戰(37)——深度學習的數學原理

TensorFlow深度學習實戰&#xff08;37&#xff09;——深度學習的數學原理0. 前言1. 反向傳播歷史2. 微積分相關概念2.1 向量2.2 導數和梯度2.3 梯度下降2.4 鏈式法則2.5 常用求導公式2.6 矩陣運算3. 激活函數4. 反向傳播4.1 前向計算4.2 反向傳播5. 交叉熵及其導數6. 批量梯度…

1.1 汽車運行滾動阻力

汽車運行阻力由4部分構成&#xff1a;滾動阻力、空氣阻力、坡度阻力、加速阻力。 1).汽車在水平道路上等速行駛時&#xff0c;必須克服來自地面的滾動阻力和來自空氣的空氣阻力。 2). 當汽車在坡道上上坡行駛時&#xff0c;還必須克服重力沿坡道的分力&#xff0c;稱為坡度阻…

e203000

1&#xff09;①BIU作為核心通信樞紐&#xff0c;主要承擔兩大功能&#xff1a;一是連接處理器核內的關鍵執行單元&#xff08;包括IFU、LSU和EAI協處理器&#xff09;&#xff0c;統一管理指令和數據的內部傳輸路徑&#xff1b;二是作為"核內計算"與"核外資源&…

Infortrend普安科技IEC私有云平臺VM解決方案

Infortrend企業云&#xff08;IEC&#xff09;內置Hypervisor運行VM。功能完整、無需額外付費。在本文中&#xff0c;我們將為您詳細介紹IEC是如何支持 VM的。市場現狀與挑戰市場現狀 虛擬化市場面臨轉型&#xff0c;主流廠商&#xff08;如 VMware&#xff09;改用訂閱制…

【代碼隨想錄算法訓練營——Day6(Day5周日休息)】哈希表——242.有效的字母異位詞、349.兩個數組的交集、202.快樂數、1.兩數之和

LeetCode題目鏈接 https://leetcode.cn/problems/valid-anagram/ https://leetcode.cn/problems/intersection-of-two-arrays/ https://leetcode.cn/problems/happy-number/ https://leetcode.cn/problems/two-sum/ 題解 242.有效的字母異位詞 這道題要想到用哈希表來做。同時注…