我理解String
和StringBuilder
(StringBuilder
可变)之间的区别,但两者之间是否有很大的性能差异?
我正在研究的程序有很多案例驱动的字符串附加(500+).使用StringBuilder
更好的选择?
是的,性能差异很大.请参阅知识库文章" 如何提高Visual C#中的字符串连接性能 ".
我一直尝试编码以获得清晰度,然后再针对性能进行优化.这比反过来要容易得多!但是,看到两者之间的应用程序存在巨大的性能差异,我现在仔细考虑一下.
幸运的是,在代码上运行性能分析以查看您花费时间的位置,然后将其修改为StringBuilder
在需要的地方使用,这是相对简单的.
为了澄清Gillian关于4字符串的内容,如果你有这样的话:
string a,b,c,d; a = b + c + d;
那么使用字符串和加号运算符会更快.这是因为(就像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();
StringBuilder是优选的,如果您在代码传递中执行多个循环或分叉...但是,对于PURE性能,如果您可以使用SINGLE字符串声明,那么性能更高.
例如:
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..
在这种情况下,StringBuild可以被认为是更易于维护的,但是不比单个字符串声明更高效.
10次中有9次...使用字符串生成器.
一方面注意:string + var也比内部使用StringBuilder的string.Format方法(通常)更高效(当有疑问时......检查反射器!)
此基准测试表明,当组合3个或更少的字符串时,常规连接更快.
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它的缓冲区在构造函数中应该有多大.
一个简单的例子来演示使用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 ms,而第二次迭代StringBuilder
花费了10 ms.
在我看来,使用StringBuilder
速度更快,速度更快.
是的,StringBuilder
在对字符串执行重复操作时提供更好的性能.这是因为所有更改都是针对单个实例进行的,因此可以节省大量时间,而不是创建新的实例String
.
String
在System
命名空间
不可变(只读)实例
当连续发生值变化时,性能会下降
线程安全
StringBuilder
(可变字符串)
在System.Text
命名空间
可变实例
显示更好的性能,因为对现有实例进行了新的更改
强烈推荐dotnet mob文章:C#中的String Vs StringBuilder.
相关的Stack Overflow问题:字符串在C#中没有变化时字符串的可变性?.
字符串与字符串生成器:
首先,您必须知道这两个类在哪个组件中生活?
所以,
字符串存在于System
名称空间中。
和
StringBuilder存在于System.Text
名称空间中。
对于字符串声明:
您必须包括System
名称空间。这样的事情。
Using System;
和
对于StringBuilder声明:
您必须包括System.text
名称空间。这样的事情。
Using System.text;
现在来实际的问题。
string和StringBuilder之间的区别是什么?
两者之间的主要区别在于:
字符串是不可变的。
和
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"
在堆栈“ name”上创建此变量并指向该“ Rehan”值。执行此行后:“ name = name +” Shah“。引用变量不再指向该对象” Rehan“,现在指向” Shah“,依此类推。
这string
是不可变的,这意味着一旦我们在内存中创建了对象,就无法更改它们。
因此,当我们隐含name
变量时,先前的对象保留在内存中,并创建了另一个新的字符串对象...
因此,根据上图,我们有五个对象,四个对象被扔掉了,根本没有使用。它们仍然保留在内存中,并且占用内存量。“垃圾收集器”对此负责,因此请清除内存中的资源。
因此,在任何情况下,当我们一遍又一遍地操作字符串时,都会有许多对象创建并保留在内存中。
这就是字符串Variable的故事。
现在,让我们看一下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次。
因此,显而易见的问题是!当我们将相同的StringBuilder更改5次时,实际上发生了什么。
这就是我们更改相同的StringBuilder 5次时发生的情况。
让我们看看图。
说明: 对于StringBuilder对象。您不会得到新对象。同一对象将在内存中发生更改,因此即使您将对象更改了10,000次,我们仍然只有一个stringBuilder对象。
您没有很多垃圾对象或未引用的stringBuilder对象,因为为什么可以更改它。它是可变的,意味着它会随时间变化吗?
差异:
在System命名空间中存在String,而在System.Text命名空间中存在Stringbuilder。
在StringBuilder是mutabe的地方,string是不可变的。
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