問題:拼接字符串,到底用哪個?
先問個實在的問題:你在代碼里怎么拼接字符串?
很多兄弟可能是這么寫的:
string str ="Hello"+" "+"World"; //當然這里只是舉個例子
也有的會在循環里這么干:
string result = "";
for (int i = 0; i < 1000; i++)
{
result += i.ToString();
}
然后有一天,你聽說了StringBuilder,據說拼接性能更好。于是你開始糾結:到底該用“+”還是StringBuilder?網上說法五花八門,有的說“+”慢成狗,有的說編譯器會優化,根本不用操心。
今天咱們就把這事掰扯清楚,以后別再憑感覺寫了。
結論:看場景,別迷信
一句話總結:
-
少量、固定次數的拼接,直接用“+”,代碼簡潔,編譯器還會幫你優化。
-
大量、循環內的拼接,尤其是次數不確定時,務必用StringBuilder,否則性能可能崩盤。
-
特殊場景(如拼接集合、格式化)考慮用string.Concat、string.Join或字符串插值,它們底層已經做了優化。
下面展開聊聊為什么。
擴展:從內存到原理,一次講透
1. 字符串的“不可變性”是罪魁禍首
C#里的字符串是引用類型,而且是不可變的。什么意思?就是你一旦創建了一個字符串,它就定型了,內容不能再改。當你試圖“修改”它時,其實是創建了一個全新的字符串對象,原來的那個等著被垃圾回收。
比如:
string s ="Hello";s = s +" World";
第二行執行時,先在內存里創建一個新字符串 "Hello World",然后把s指向它,原來的 "Hello" 就成了沒人要的孤兒,等著GC來收。
這種設計有好處(線程安全、哈希緩存等),但拼接時就成了性能殺手。
2. “+”拼接的真相:分情況討論
情況A:編譯期就能確定的拼接
string str ="Hello"+" "+"World";
這種代碼在編譯時,編譯器直接把它優化成了 "Hello World",生成的IL里只有一個字符串。所以運行時沒有任何拼接開銷,放心用。
情況B:拼接中包含變量
string name ="剛子";
string msg ="Hello, "+ name +"!";
這種編譯器會把它轉成string.Concat調用,比如:
string msg =string.Concat("Hello, ", name,"!");
string.Concat內部會根據參數數量,一次性計算出最終字符串長度,然后分配內存,把各部分拷貝進去。一次拼接,一次分配,效率其實不錯。所以這種少量“+”拼接,完全沒問題。
情況C:循環內反復拼接
string result = "";
for (int i = 0; i < 10000; i++)
{
result += i.ToString();
}
這里每循環一次,都會產生一個新的字符串,而且一次比一次大。比如:
3. StringBuilder 為什么快?
StringBuilder內部維護了一個可變的字符數組(char[])。當你 Append 時,它會直接往數組里寫,空間不夠了就自動擴容(通常是翻倍)。所有追加操作都在同一個數組里進行,只有最后調用ToString()時才真正創建一次字符串。
所以上面的循環用StringBuilder改寫:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
sb.Append(i.ToString());
}
string result = sb.ToString();
4. 來點實測數據
我寫了個簡單測試(環境:.NET 6, Release模式,各執行10萬次拼接):
| 方式 |
10次拼接 |
1000次拼接 |
10萬次拼接 |
| +(循環內) |
<1ms |
15ms |
爆炸(幾秒) |
| StringBuilder |
<1ms |
<1ms |
8ms |
注意,“+”在小數量時差距不大,但一旦數量上去,直接崩盤。
5. 其他拼接方式,也得拎清楚
string.Concat
前面說了,編譯器會把多個“+”優化成Concat。如果自己手動拼接一組已知的對象,用Concat比循環“+”強。
string.Join
拼接集合時最強:
string csv = string.Join(",", numbers); // numbers是List<int>
內部也是用StringBuilder實現的,比自己手寫循環高效。
字符串插值($"...")
string msg = $"Hello, {name}! You are {age} years old.";
編譯后也是string.Format(或者在某些情況下轉成Concat)。它簡潔明了,適合格式化場景,但如果插值數量很多且頻繁調用,注意string.Format內部也有開銷(解析格式字符串、參數裝箱等)。不過日常用沒問題。
6. 最佳實踐總結
-
原則1:能用一句話寫完的拼接,直接用“+”(編譯器會優化)。
-
原則2:循環內拼接,尤其次數不確定時,無腦用StringBuilder。
-
原則3:拼接集合用string.Join。
-
原則4:格式化用字符串插值或string.Format,但避免在熱點代碼中頻繁使用帶復雜格式的插值。
-
原則5:如果提前能預估最終長度,給StringBuilder預設容量(new StringBuilder(預期長度)),減少擴容。
7. 面試官追問:StringBuilder就一定比“+”快嗎?
不一定。如果只是兩三個字符串拼接,用“+”編譯成Concat,可能比創建StringBuilder對象開銷還小。畢竟new對象也有成本。所以“少量固定次數”時,“+”更優。
另外,如果你用的是.NET Core 2.1+,string.Create方法允許你直接操作字符數組,實現最高效的拼接,但那是高階玩法,日常用不上。
總結
字符串拼接這事,看起來小,但用錯了地方,真能把程序拖垮。別再憑感覺了,記住三個關鍵詞:少量用“+”、循環用Builder、集合用Join。寫出性能好的代碼,從選對拼接方式開始。
?轉自https://www.cnblogs.com/shenchuanchao/p/19744564
該文章在 2026/4/3 17:14:22 編輯過