20.4 使用StringBuilder类
鉴于String在某些情况下的效率低下问题,例如在循环中改变字符串的值,将产生非常多的中间对象,这些对象将造成存储浪费和效率降低。为了应对这种问题,BCL提供了StringBuilder类用以高效地处理字符串,该类位于System.Text命名空间。StringBuilder类是一种可变字符串,使用StringBuilder类可以在不创建新的对象的情况下改变字符串的值,包括追加、删除、修改等操作。最常用的初始化StringBuilder类的新实例的方式有三种:
❑StringBuilder sb1=new StringBuilder();
❑StringBuilder sb2=new StringBuilder(64);
❑StringBuilder sb3=new StringBuilder("Hello world!");
第一种方式实际上使用了默认容量初始化StringBuilder类的新实例,这个默认的容量为32。容量定义了可存储在当前实例所分配的内存中的最大字符数,如果当前实例中存储的字符数超过了容量值,则StringBuilder对象将分配更多内存来存储它们。默认的字符串值为String.Empty。
将StringBuilder和String类对比,String值的追加操作每次都会分配新的内存,而StringBuilder仅当StringBuilder的对象缓冲区太小以至于无法容纳新数据时才分配内存。因此,当字符串追加操作并不多时,最好还是使用String类。某些情况下,编译器甚至会将多个字符串的联结操作组合到一个操作中。但如果需要任意多次追加字符串,例如在一个循环中,使用StringBuilder类比使用String类性能更高。尽管StringBuilder有着诸多好处,但创建一个StringBuilder实例比创建一个String实例需要花费更多的内存资源,因此使用StringBuilder也是要讲场合的,这一点需要读者注意。
接下来,通过一段示例代码来演示StringBuilder的用法,在示例代码中分别使用StringBuilder类和String类创建一个10 000行的字符串,其中每行包含一个日期字符串,最后将日期中的斜杠"/"替换成"-"符号。我们会分别统计使用两个类各自所需要的时间,以此来比较在多次改变一个字符串的值时这两个类的效率差别。具体请参看代码清单20-10。
代码清单20-10 StringBuilder和String类的效率对比
using System;
using System.Text;
namespace ProgrammingCSharp4
{
class StringSample
{
public static void Main()
{
const int numberOfLoops=10000;
StringBuilder myBuilder=new StringBuilder();
DateTime endDate=DateTime.Now.AddDays(numberOfLoops);
DateTime theDate=DateTime.Now;
DateTime startTime=DateTime.Now;
const int aproxLengthOfNewString=11*numberOfLoops;
myBuilder.Capacity=aproxLengthOfNewString;
while(theDate<endDate)
{
myBuilder.AppendLine(theDate.ToShortDateString());
theDate=theDate.AddDays(1);
}
myBuilder.Replace("/","-");
string testString=myBuilder.ToString();
TimeSpan interval=TimeSpan.FromTicks(DateTime.Now.Ticks-startTime.Ticks);
Console.WriteLine(
“StringBuilder:花费{0}毫秒追加{1}个字符串,字符串长度为{2}.”,
interval.TotalMilliseconds,numberOfLoops,testString.Length);
Console.WriteLine();
testString=“";
theDate=DateTime.Now;
startTime=DateTime.Now;
while(theDate<endDate)
{
testString+=theDate.ToShortDateString()+"\n";
theDate=theDate.AddDays(1);
}
testString.Replace("/","-");
interval=TimeSpan.FromTicks(DateTime.Now.Ticks-startTime.Ticks);
Console.WriteLine(
“String:花费{0}毫秒追加{1}个字符串,字符串长度为{2}”,
interval.TotalMilliseconds,numberOfLoops,testString.Length);
Console.WriteLine();
}
}
}
为了让结果更准确,我们运行了3次,如下是这3次的运行结果:
第一次运行:
StringBuilder:花费46.875毫秒追加10000个字符串,字符串长度为109588.
String:花费4656.25毫秒追加10000个字符串,字符串长度为99588
第二次运行:
StringBuilder:花费46.875毫秒追加10000个字符串,字符串长度为109588.
String:花费4765.625毫秒追加10000个字符串,字符串长度为99588
第三次运行:
StringBuilder:花费62.5毫秒追加10000个字符串,字符串长度为109588.
String:花费4671.875毫秒追加10000个字符串,字符串长度为99588
可见,此时StringBuilder的工作效率达到了String的近100倍。因此基于性能考虑,在循环中操作字符串的追加操作时,应该使用StringBuilder代替String。