我知道String
和StringBuilder
之间的区别(StringBuilder
可以被修改),但是这两者之间有很大的性能差异吗?
我正在开发的程序具有许多基于情况的字符串追加(500+)。使用StringBuilder
是更好的选择吗?
我知道String
和StringBuilder
之间的区别(StringBuilder
可以被修改),但是这两者之间有很大的性能差异吗?
我正在开发的程序具有许多基于情况的字符串追加(500+)。使用StringBuilder
是更好的选择吗?
是的,性能差异很大。请参阅KB文章“如何提高Visual C#中字符串连接的性能”。
我一直尝试首先编写清晰易懂的代码,然后再优化性能。这比反过来做容易得多!然而,看到我的应用程序之间的巨大性能差异后,我现在会更加谨慎地考虑它。
幸运的是,相对而言,运行性能分析以查看代码花费时间的位置并进行修改以在需要时使用StringBuilder
是相对简单的。
为了澄清Gillian关于4字符串的说法,如果你有这样的东西:
string a,b,c,d;
a = b + c + d;
如果是使用字符串和加号运算符,速度会更快。这是因为它(像Java一样,就像Eric指出的那样)在内部自动使用StringBuilder(实际上,它使用了一个与StringBuilder相同的原语)。
但是,如果你要做的更接近以下情况:
string a,b,c,d;
a = a + b;
a = a + c;
a = a + d;
那么您需要显式地使用 StringBuilder。 .Net 在这里不会自动创建一个 StringBuilder,因为这是没有意义的。在每行结尾处,“a”必须是(不可变的)字符串,因此它必须在每行上创建和处理一个 StringBuilder。为了速度,您需要在构建完成之前一直使用同一个 StringBuilder:
string a,b,c,d;
StringBuilder e = new StringBuilder();
e.Append(b);
e.Append(c);
e.Append(d);
a = e.ToString();
a
是一个局部变量,并且它所引用的对象没有被分配给其他变量(可能可以被另一个线程访问),那么一个好的优化器可以确定在这些代码行序列中没有其他代码访问a
;只有a
的最终值才是重要的。因此,它可以将这三行代码视为a = b + c + d;
编写的方式。 - ToolmakerSteve如果您的代码需要多次循环或分叉,那么StringBuilder是更可取的选择...但是,就性能而言,如果您可以使用单个字符串声明,那么它会更高效。
例如:
string myString = "Some stuff" + var1 + " more stuff"
+ var2 + " other stuff" .... etc... etc...;
比...更高效。StringBuilder sb = new StringBuilder();
sb.Append("Some Stuff");
sb.Append(var1);
sb.Append(" more stuff");
sb.Append(var2);
sb.Append("other stuff");
// etc.. etc.. etc..
在这种情况下,StringBuilder 可以被认为比单个字符串声明更易维护,但不比其更高效。但十之九,使用 StringBuilder 更好。使用String
拼接与StringBuilder
比较速度的简单示例:
System.Diagnostics.Stopwatch time = new Stopwatch();
string test = string.Empty;
time.Start();
for (int i = 0; i < 100000; i++)
{
test += i;
}
time.Stop();
System.Console.WriteLine("Using String concatenation: " + time.ElapsedMilliseconds + " milliseconds");
结果:
使用字符串拼接:15423毫秒
StringBuilder test1 = new StringBuilder();
time.Reset();
time.Start();
for (int i = 0; i < 100000; i++)
{
test1.Append(i);
}
time.Stop();
System.Console.WriteLine("Using StringBuilder: " + time.ElapsedMilliseconds + " milliseconds");
结果:
使用 StringBuilder: 10 毫秒
因此,第一次迭代花费了15423毫秒,而使用StringBuilder
的第二次迭代只花费了10毫秒。
在我看来,使用StringBuilder
更快,快了很多。
这个基准测试显示,当连接三个或更少的字符串时,常规连接比较快。
http://www.chinhdo.com/20070224/stringbuilder-is-not-always-faster/
StringBuilder可以显著提高内存使用效率,特别是在将500个字符串连接在一起的情况下。
考虑以下示例:
string buffer = "The numbers are: ";
for( int i = 0; i < 5; i++)
{
buffer += i.ToString();
}
return buffer;
内存中会发生什么?以下字符串被创建:
1 - "The numbers are: "
2 - "0"
3 - "The numbers are: 0"
4 - "1"
5 - "The numbers are: 01"
6 - "2"
7 - "The numbers are: 012"
8 - "3"
9 - "The numbers are: 0123"
10 - "4"
11 - "The numbers are: 01234"
12 - "5"
13 - "The numbers are: 012345"
在我们将这五个数字添加到字符串末尾后,我们创建了13个字符串对象!其中12个是无用的!哇!
StringBuilder解决了这个问题。它不是像我们经常听到的那样的“可变字符串”(.NET中的所有字符串都是不可变的)。它通过保持一个内部缓冲区,即char数组来工作。调用Append()或AppendLine()将字符串添加到char数组末尾的空白处;如果数组太小,则创建一个新的、更大的数组,并将缓冲区复制到那里。因此,在上面的例子中,StringBuilder可能只需要一个数组来包含对字符串的所有5个添加——这取决于其缓冲区的大小。您可以在构造函数中告诉StringBuilder它的缓冲区应该有多大。
i.ToString()
。所以使用 StringBuilder,您仍然必须创建6 + 1个字符串;它将创建13个字符串减少到7个字符串。但这仍然是错误的看法;创建六个数字字符串并不重要。底线:您根本不应该提到由i.ToString()
创建的六个字符串;它们不是效率比较的一部分。 - ToolmakerSteveString Vs String Builder:
首先你需要知道这两个类分别在哪个程序集中。
因此,
string 存在于 System
命名空间中。
以及
StringBuilder 存在于 System.Text
命名空间中。
对于 string 的声明:
需要包含 System
命名空间。
类似于这样。
Using System;
以及
对于 StringBuilder 的声明:
需要包含 System.text
命名空间。
类似于这样。
Using System.text;
现在进入实际问题。
什么是 string 和 StringBuilder 之间的区别?
这两者之间的主要区别是:
string 是不可变的。
以及
StringBuilder 是可变的。
接下来让我们讨论一下 不可变的 和 可变的 的区别。
可变的: 意味着可以更改。
不可变的: 意味着无法更改。
例如:
using System;
namespace StringVsStrigBuilder
{
class Program
{
static void Main(string[] args)
{
// String Example
string name = "Rehan";
name = name + "Shah";
name = name + "RS";
name = name + "---";
name = name + "I love to write programs.";
// Now when I run this program this output will be look like this.
// output : "Rehan Shah RS --- I love to write programs."
}
}
}
因此,在这种情况下,我们将更改同一对象5次。
那么显而易见的问题是!当我们5次更改相同的字符串时,在幕后实际发生了什么。
这就是当我们5次更改相同的字符串时发生的事情。
让我们看一下这张图。
解释:
当我们首先将该变量“name”初始化为“Rehan”,即string name = "Rehan"
,此变量在栈上创建并指向该“Rehan”值。执行此行之后:“name = name + "Shah"。”引用变量不再指向该对象“Rehan”,它现在指向“Shah”等。
因此,string
是不可变的,这意味着一旦我们在内存中创建了对象,就无法更改它们。
因此,当我们连接name
变量时,以前的对象仍然保留在内存中,并且另一个新的字符串对象被创建...
因此,从上图中我们可以得出五个对象,其中四个对象被抛弃,它们根本没有被使用。它们仍然留在内存中并占用一定的内存。 “垃圾回收器”负责清理内存中的这些资源。
因此,在字符串的情况下,每当我们反复操纵字符串时,我们都会创建许多对象并保留在内存中。
因此,这就是字符串变量的故事。
现在让我们看一下StringBuilder对象。例如:
using System;
using System.Text;
namespace StringVsStrigBuilder
{
class Program
{
static void Main(string[] args)
{
// StringBuilder Example
StringBuilder name = new StringBuilder();
name.Append("Rehan");
name.Append("Shah");
name.Append("RS");
name.Append("---");
name.Append("I love to write programs.");
// Now when I run this program this output will be look like this.
// output : "Rehan Shah Rs --- I love to write programs."
}
}
}
所以在这种情况下,我们将更改同一对象5次。
那么显而易见的问题是!当我们更改同一字符串生成器5次时,在幕后实际发生了什么。
这就是当我们更改同一字符串生成器5次时会发生什么。
解释: 对于StringBuilder对象。你不会得到新的对象。同一对象将在内存中进行更改,因此即使您更改对象并说10000次,我们仍将只有一个stringBuilder对象。
您没有大量的垃圾对象或非引用stringBuilder对象,因为它可以被更改。它是可变的,意味着随着时间的推移而更改?
区别:
是的,StringBuilder
在执行重复操作时可以提供更好的性能。这是因为所有的更改都是针对单个实例进行的,因此它可以节省很多时间,而不是像String
一样创建一个新实例。
String
System
命名空间中StringBuilder
(可变字符串)
System.Text
命名空间中强烈推荐 dotnet mob 文章:C# 中的 String Vs StringBuilder.
相关的 Stack Overflow 问题:当字符串不改变时字符串的可变性?.
StringBuilder可以减少分配和赋值的次数,但会增加额外的内存使用。如果使用得当,它可以完全消除编译器反复分配越来越大的字符串直到找到结果的需要。
string result = "";
for(int i = 0; i != N; ++i)
{
result = result + i.ToString(); // allocates a new string, then assigns it to result, which gets repeated N times
}
对比。
String result;
StringBuilder sb = new StringBuilder(10000); // create a buffer of 10k
for(int i = 0; i != N; ++i)
{
sb.Append(i.ToString()); // fill the buffer, resizing if it overflows the buffer
}
result = sb.ToString(); // assigns once