我知道String
和StringBuilder
之间的区别(StringBuilder
可以被修改),但是这两者之间有很大的性能差异吗?
我正在开发的程序具有许多基于情况的字符串追加(500+)。使用StringBuilder
是更好的选择吗?
我知道String
和StringBuilder
之间的区别(StringBuilder
可以被修改),但是这两者之间有很大的性能差异吗?
我正在开发的程序具有许多基于情况的字符串追加(500+)。使用StringBuilder
是更好的选择吗?
StringBuilder
更适合从许多非常量值构建字符串。
如果您正在从许多常量值(例如HTML或XML文档中的多行值或其他文本块)构建字符串,则可以通过仅附加到相同的字符串来完成,因为几乎所有编译器都会执行“常量折叠”过程,即在具有大量常量操作时减少解析树(当您编写类似于int minutesPerYear = 24 * 365 * 60
的内容时也使用它)。对于简单情况下互相追加的非常量值,.NET编译器将将您的代码减少为类似于StringBuilder
的内容。
但是,当您的追加无法被编译器简化为更简单的形式时,您需要一个StringBuilder
。正如fizch所指出的那样,这更可能发生在循环内部。
在之前的回答基础上,当我面对这种问题时,我总是首先创建一个小型测试应用程序。在该应用程序内,对两种情况进行一些计时测试,亲自查看哪种更快。
我的观点是,追加500多个字符串条目肯定应该使用StringBuilder。
StringBuilder
是更高效的,但除非您正在进行大量字符串修改,否则您将看不到该性能。
下面是一个快速的代码块,以说明其性能。正如您所看到的,只有当您进行大量迭代时才会开始看到主要的性能提升。
如您所见,200,000 次迭代花费了 22 秒,而使用 StringBuilder
的 1 百万次迭代几乎瞬间完成。
string s = string.Empty;
StringBuilder sb = new StringBuilder();
Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());
for (int i = 0; i <= 50000; i++)
{
s = s + 'A';
}
Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();
Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());
for (int i = 0; i <= 200000; i++)
{
s = s + 'A';
}
Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();
Console.WriteLine("Beginning Sb append at " + DateTime.Now.ToString());
for (int i = 0; i <= 1000000; i++)
{
sb.Append("A");
}
Console.WriteLine("Finished Sb append at " + DateTime.Now.ToString());
Console.ReadLine();
以上代码的结果:
开始字符串 + 于2013年1月28日16:55:40。
结束字符串 + 于2013年1月28日16:55:40。
开始字符串 + 于2013年1月28日16:55:40。
结束字符串 + 于2013年1月28日16:56:02。
开始Sb追加于2013年1月28日16:56:02。
完成Sb追加于2013年1月28日16:56:02。
我相信如果你需要连接超过4个字符串,StringBuilder会更快。此外,它还可以执行一些很酷的操作,比如AppendLine。
使用字符串进行连接可能导致 O(n^2)
的运行时复杂度。
如果您使用 StringBuilder
,则需要执行的内存复制要少得多。如果您可以估计最终 String
的大小,则可以使用 StringBuilder(int capacity)
来提高性能。即使您不是很精确,您只需增加几次 StringBuilder
的容量,也可以帮助提高性能。
StringBuilder
存储字符串之前,调用 EnsureCapacity(int capacity)
方法可以显著提高性能。通常我会在实例化后的代码行上调用它。这与您像这样实例化 StringBuilder
具有相同的效果:var sb = new StringBuilder(int capacity);
这个调用提前分配所需的内存,在多个 Append()
操作期间减少了更少的内存分配。你必须根据需要做出明智的猜测,但对于大多数应用程序而言,这不应该太困难。我通常会对稍微多一点的内存容错(我们说的是1k左右)。
StringBuilder
实例化后,没有必要立即调用 EnsureCapacity
。只需像这样实例化 StringBuilder
:var sb = new StringBuilder(int capacity)
。 - David Ferenczy RogožanString和StringBuilder实际上都是不可变的,但StringBuilder内置了缓冲区,可以更有效地管理其大小。当StringBuilder需要调整大小时,它会在堆上重新分配空间。默认情况下,它的大小为16个字符,您可以在构造函数中设置它。
例如:
StringBuilder sb = new StringBuilder(50);