尽管 StringBuilder 和 String 都表示字符序列,但它们的实现方式不同。
String 是不可变类型。 也就是说,出现的用于修改对象的每个操作 String 实际上都会创建一个新的字符串。
对于执行大量字符串操作的例程 (例如在循环中多次修改字符串的应用程序) ,重复修改字符串可能会显著降低性能。 替代方法是使用 StringBuilder ,它是一个可变字符串类。 可变性是指在创建类的实例后,可以通过追加、移除、替换或插入字符来修改它。 StringBuilder对象维护缓冲区以容纳对字符串的扩展。 如果空间可用,则会将新数据追加到缓冲区;否则,将分配一个新的更大的缓冲区,将原始缓冲区中的数据复制到新缓冲区,然后将新数据追加到新缓冲区。
尽管 StringBuilder 类通常比 String 类提供更好的性能,但你不应在 String 每次需要操作字符串时自动将替换为StringBuilder 。 性能取决于字符串的大小、要为新字符串分配的内存量、正在执行应用程序的系统以及操作的类型。 应该准备好测试应用程序,以确定实际是否 StringBuilder 提供显著的性能改进。
请考虑在以下情况下使用 String 类:
-
当你的应用将对字符串进行的更改数量很小时。 在这些情况下, StringBuilder 可能会提供可忽略不及或不会提高性能。
-
当你执行固定数量的串联操作时,尤其是字符串文本。 在这种情况下,编译器可能会将串联操作合并为单个操作。
-
在生成字符串时,必须执行大量的搜索操作。 StringBuilder类缺少搜索方法,如
IndexOf
或StartsWith
。 StringBuilder对于这些操作,必须将对象转换为 String ,这可能会使使用不会带来性能优势 StringBuilder 。 有关详细信息,请参阅在 StringBuilder 对象中搜索文本 部分。
请考虑在以下情况下使用 StringBuilder 类:
-
如果希望应用在设计时对字符串进行未知数量的更改 (例如,当你使用循环来连接包含用户输入) 的随机数量的字符串时。
-
希望应用对字符串进行大量更改时。
StringBuilder 的工作方式
StringBuilder.Length属性指示对象当前包含的字符数 StringBuilder 。 如果向对象添加字符 StringBuilder ,则其长度将增加,直到它等于属性的大小 StringBuilder.Capacity ,该大小定义对象可以包含的字符数。 如果添加的字符数导致对象的长度 StringBuilder 超过其当前容量,则分配新内存,属性的值 Capacity 翻倍,新字符将添加到 StringBuilder 对象中,并 Length 调整其属性。 对象的额外内存 StringBuilder 会动态分配,直到达到属性定义的值 StringBuilder.MaxCapacity 。 达到最大容量时,不能为该对象分配更多的内存 StringBuilder ,尝试添加字符或将其扩展到超出其最大容量后,会引发 ArgumentOutOfRangeException 或 OutOfMemoryException 例外。
下面的示例演示了 StringBuilder 对象如何分配新内存并动态增加其容量,因为分配给对象的字符串会展开。 该代码 StringBuilder 通过调用默认 (无参数) 构造函数来创建对象。 此对象的默认容量为16个字符,其最大容量超过2000000000个字符。 追加字符串 "This is a 句子"。 导致新的内存分配,因为字符串长度 (19 个字符) 超过对象的默认容量 StringBuilder 。 对象的容量长度为32个字符,添加新字符串,并且对象的长度现在等于19个字符。 然后,该代码将追加字符串 "This is 其他句子"。 对象的值 StringBuilder 11 次。 每当追加操作导致对象的长度 StringBuilder 超过其容量时,其现有容量就会加倍, Append 操作成功。
using System;
using System.Reflection;
using System.Text;
public class Example
{
public static void Main()
{
StringBuilder sb = new StringBuilder();
ShowSBInfo(sb);
sb.Append("This is a sentence.");
ShowSBInfo(sb);
for (int ctr = 0; ctr <= 10; ctr++) {
sb.Append("This is an additional sentence.");
ShowSBInfo(sb);
}
}
private static void ShowSBInfo(StringBuilder sb)
{
foreach (var prop in sb.GetType().GetProperties()) {
if (prop.GetIndexParameters().Length == 0)
Console.Write("{0}: {1:N0} ", prop.Name, prop.GetValue(sb));
}
Console.WriteLine();
}
}
// The example displays the following output:
// Capacity: 16 MaxCapacity: 2,147,483,647 Length: 0
// Capacity: 32 MaxCapacity: 2,147,483,647 Length: 19
// Capacity: 64 MaxCapacity: 2,147,483,647 Length: 50
// Capacity: 128 MaxCapacity: 2,147,483,647 Length: 81
// Capacity: 128 MaxCapacity: 2,147,483,647 Length: 112
// Capacity: 256 MaxCapacity: 2,147,483,647 Length: 143
// Capacity: 256 MaxCapacity: 2,147,483,647 Length: 174
// Capacity: 256 MaxCapacity: 2,147,483,647 Length: 205
// Capacity: 256 MaxCapacity: 2,147,483,647 Length: 236
// Capacity: 512 MaxCapacity: 2,147,483,647 Length: 267
// Capacity: 512 MaxCapacity: 2,147,483,647 Length: 298
// Capacity: 512 MaxCapacity: 2,147,483,647 Length: 329
// Capacity: 512 MaxCapacity: 2,147,483,647 Length: 360