CodeGym /課程 /JAVA 25 SELF /StringBuilder 與 StringBuffer

StringBuilder 與 StringBuffer

JAVA 25 SELF
等級 9 , 課堂 5
開放

1. 不可變的字串:朋友還是敵人?

在 Java 中,String 類別是不可變的(immutable)。這表示建立後的字串無法被更改。每次你「修改」字串,例如透過 +concat() 加上內容,事實上都會建立一個新的物件,而舊的物件則等待垃圾回收。

範例:

String s = "Hello";
s = s + " world!";
System.out.println(s); // Hello world!

看起來好像變數 s 被修改了,但實際上是建立了新的字串 "Hello world!",而舊的 "Hello" 會留在記憶體中,直到被垃圾回收器清理。如果這類操作很多——例如在迴圈中——程式會變慢且耗用多餘記憶體。

想像你在堆疊積木,每次想加一塊新積木,都得把整座塔重建一遍。這可不太經濟,對吧?在頻繁修改的情況下,Java 中一般的 String 就是這樣運作的。

2. StringBuilder:快速的字串建構器

StringBuilder(位於 java.lang,無需匯入)是用於高效組裝與修改字串的工具。它是可變的(mutable):可以新增、刪除、插入字元與子字串,而不必為每次操作建立新物件。

類比:
如果 String 是一塊混凝土板,那麼 StringBuilder 就像 LEGO 積木:想加就加、想拆就拆,不必把整個結構重來。

如何建立 StringBuilder

StringBuilder sb = new StringBuilder(); // 空的
StringBuilder sb2 = new StringBuilder("初始值");

主要方法

方法 說明 使用範例
append(...)
在末尾加入字串、數字、字元等
sb.append("Java");
insert(index, ...)
在指定位置插入值
sb.insert(0, "Hello ");
delete(start, end)
刪除從 start(含)到 end(不含)的字元
sb.delete(0, 5);
replace(start, end, str)
以其他內容取代部分字串
sb.replace(0, 4, "Hi");
reverse()
將字串反轉
sb.reverse();
toString()
轉換為一般字串
String s = sb.toString();
setLength(newLen)
將字串截斷或補齊至指定長度
sb.setLength(3);

使用範例

StringBuilder sb = new StringBuilder();
sb.append("你好,");
sb.append("世界!");
System.out.println(sb); // 你好,世界!

sb.insert(7, "Java "); // 在「你好,」之後插入 "Java "
System.out.println(sb); // 你好,Java 世界!

sb.replace(8, 12, "其他"); // 把 "Java" 替換成「其他」
System.out.println(sb); // 你好,其他 世界!

sb.reverse();
System.out.println(sb); // 反轉後的內容

3. StringBuffer:具備多執行緒保護的老大哥

StringBuilderStringBuffer 有什麼差別?

  • StringBuilder 速度快,但非執行緒安全(未同步)。
  • StringBuffer 較慢,但執行緒安全(已同步)。

如果你的應用只在單執行緒中運作——請使用 StringBuilder(更快)。若有多個執行緒可能同時修改同一段字串——請使用 StringBuffer

4. 何時使用 StringBuilder 取代 String?

情境

  • 在迴圈或組裝大型文字時,頻繁地對字串進行新增、刪除、插入。
  • 從陣列/清單組裝字串(例如 CSV、HTML、報表等)。
  • 在有大量字串操作的文字剖析與處理情境。

範例:從陣列組裝字串

不佳的方式(透過 String+):

String[] names = {"Ivan", "Pyotr", "Maria"};
String result = "";

for (int i = 0; i < names.length; i++) 
{
    result += names[i];
    if (i < names.length - 1) 
    {
        result += ", ";
    }
}
System.out.println(result);

較佳的方式(透過 StringBuilder):

String[] names = {"Ivan", "Pyotr", "Maria"};
StringBuilder sb = new StringBuilder();

for (int i = 0; i < names.length; i++) 
{
    sb.append(names[i]);
    if (i < names.length - 1) 
    {
        sb.append(", ");
    }
}
System.out.println(sb.toString());

差異:第一種做法每一步都會建立新的字串;第二種做法則是持續擴充同一個 StringBuilder 物件。

5. 效能比較:String vs StringBuilder


// 使用 String
long t1 = System.currentTimeMillis();
String s = "";
for (int i = 0; i < 10000; i++)
{
    s += i + " ";
}
long t2 = System.currentTimeMillis();
System.out.println("String: " + (t2 - t1) + " 毫秒");

// 使用 StringBuilder
t1 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
    sb.append(i).append(" ");
}
s = sb.toString();
t2 = System.currentTimeMillis();
System.out.println("StringBuilder: " + (t2 - t1) + " 毫秒");

結論:在多次串接的情況下,StringBuilder 在多數情境通常快上數個數量級。

6. 實用細節

能否使用 String 的方法?
StringBuilder 有其專屬方法。若要取得字串,請呼叫 toString()

StringBuilder sb = new StringBuilder("Hello");
String s = sb.toString(); // 現在 s 是一般的字串

可以用 equals 比較 StringBuilder 嗎?
注意:sb1.equals(sb2) 比較的是參考(位址),而不是內容。請這樣比較:

if (sb1.toString().equals(sb2.toString())) 
{
    // 內容相同
}

可以把 StringBuilder 傳給 System.out.println 嗎?
可以。System.out.println 會自動呼叫 toString()

可以按索引讀取字元嗎?
可以,使用 charAt(int index),與一般字串相同。

7. 使用 StringBuilder 與 StringBuffer 的常見錯誤

錯誤 1:StringBuilderequals 或運算子 == 來比較兩個物件。這些只會比較參考,不會比較內容。請使用 toString() 並比較字串。

錯誤 2:在需要 String 的地方(方法回傳、記錄、傳給 API)忘記呼叫 toString()

錯誤 3:為了幾次簡單的串接就使用 StringBuilder。像 "Hello, " + name 這樣的寫法可讀性好且足夠高效。

錯誤 4:在迴圈裡用 + 串接 String。這在時間與記憶體上都不高效——請改用 StringBuilder

錯誤 5:在沒有必要時混淆 StringBufferStringBuilder。若沒有多執行緒共同修改同一字串的需求——請選用 StringBuilder

錯誤 6:不加思索地用 setLength() 截斷 StringBuilder 的內容。請務必確認新長度的正確性;超出新長度的資料會遺失。

1
問卷/小測驗
字串處理,等級 9,課堂 5
未開放
字串處理
字串處理
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION