您的位置:首页 > 产品设计 > UI/UE

深度分析String类型与StringBuilder类型

2009-11-29 14:57 127 查看
String类型在MSDN中的描述如下:

  String is called immutable because its value cannot be modified once it has been created. Methods that appear to modify a String actually return a new String containing the modification. If it is necessary to modify the actual contents of a string-like object, use the System.Text.StringBuilder class.

  基本意思是:

  String对象是一成不变的,一旦创建其值便无法修改。任何对String对象的修改其实是返回包含修改的新的String对象。如果有必要像修改对象一样修改的字符串的实际内容,建议使用System.Text.StringBuilder
  也就是说,如果我们要改变一个字符串里的内容,其它是先创建一个新的字符串(当然要分配新的内存),然后把要修改的内容存入新地址,然后返回新内存地址。
System.Text.StringBuilder则不用新创建新对象,这样,System.Text.StringBuilder用于连接字符的速度就远比String要快的多。

  关于C#语言中String类型和StringBuilder类型之间的差别,互联网上有很多种解释,不过其中大多数关注的只是他们在使用时时间复杂度上的差别,其实在空间复杂度上,他们之间的差距也是巨大的。

  实验说明:1.该实验是在控制台应用程序下实施的

       2.通过多次修改一个StringBuilder或String变量来监测它们消耗的时间和内存

  在空间复杂度的监测上,我主要是监测他们在内存消耗上的差别(另外它们占用的交换文件大小也不一样)。要监测内存的改变,这里就需要用到API函数来获取系统信息。所以在调用API函数之前,必须要先导入System.Runtime.InteropServices这个命名空间,然后在创建一个用来获取内存信息的类:

public class MemoryInfo
{
[DllImport("kernel32")]
public static extern void GlobalMemoryStatus(ref MEMORY_INFO meminfo);
//定义内存信息结构
[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_INFO
{
public uint dwLength;
public uint dwMemoryLoad;
public uint dwTotalPhys;
public uint dwAvailPhys;
public uint dwTotalPageFile;
public uint dwAvailPageFile;
public uint dwTotalVirtual;
public uint dwAvailVirtual;
}

  //获取系统信息

public void GetMemoryInfo()
{
MEMORY_INFO MemInfo;
MemInfo = new MEMORY_INFO();
GlobalMemoryStatus(ref MemInfo);
Console.WriteLine(MemInfo.dwMemoryLoad.ToString() + "%内存正在使用");//
Console.WriteLine("物理内存:" +(double.Parse(MemInfo.dwTotalPhys.ToString())/1048576).ToString()+ "MB。");
Console.WriteLine("可用物理内存" +(double.Parse(MemInfo.dwAvailPhys.ToString())/1048576).ToString() + "MB。");
}

  //获取当前状态的可用内存大小,并输出到控制台

public void GetAvailMemory(ref string befLoop)
{
MEMORY_INFO MemInfo;
MemInfo = new MEMORY_INFO();
GlobalMemoryStatus(ref MemInfo);
befLoop=MemInfo.dwAvailPhys.ToString();
Console.WriteLine("当前可用内存大小为:"+double.Parse(befLoop)/1048576+"MB。");
}

//控制台输出循环前后消耗的内存大小
public void MemoryCostPrint(string beforeLoop,string afterLoop)
{
Double bef = Double.Parse(beforeLoop);
Double aft = Double.Parse(afterLoop);
Console.WriteLine("共消耗:"+((bef-aft) / 1048576).ToString()+"MB内存。");
}
}

  (1)时间复杂度分析

  主要代码片段:
Console.WriteLine("********************************String类型******************************");
Console.WriteLine("----循环前:");
MemInfo.GetAvailMemory(ref befLoop); //获取循环前可用内存的大小
string str = "a";
Console.WriteLine("变量长度:" + str.Length.ToString());
beginTime = DateTime.Now; //获取循环前的时间
for (int i = 0; i < count; i++) //count=10000
{
str += "a";
}
TimeSpan time = (DateTime.Now - beginTime); //获得循环前后的时间差
Console.WriteLine("----循环后:");
MemInfo.GetAvailMemory(ref aftLoop); //获取循环后可用内存的大小
MemInfo.MemoryCostPrint(befLoop, aftLoop);
Console.WriteLine("变量长度:" + str.Length.ToString());
Console.WriteLine("共消耗时间:" + (time.TotalMilliseconds / 1000).ToString() + "秒。");

  运行结果:




  (2)空间复杂度分析

  主要代码:

Console.WriteLine("********************************StringBuilder类型******************************");
System.DateTime beginTime_strBui = new DateTime();
MemInfo.GetAvailMemory(ref strBui_befLoop);
StringBuilder strBui = new StringBuilder("a");
Console.WriteLine("循环前变量长度:" + strBui.Length.ToString());
beginTime_strBui = DateTime.Now;
for (int i = 0; i < count; i++)
{
strBui.Append("a");
}
TimeSpan strBui_time = (DateTime.Now - beginTime_strBui);
MemInfo.GetAvailMemory(ref strBui_aftLoop);
MemInfo.MemoryCostPrint(strBui_befLoop, strBui_aftLoop);
Console.WriteLine("循环后变量长度:"+strBui.Length.ToString());
Console.WriteLine("共消耗时间:" + strBui_time.TotalMilliseconds.ToString() + "毫秒.");
  运行结果:





Str【String类型】和StrBui【StringBuilder类型】两个变量在循环前后的值都一样,循环前为"a",循环后为一万零一个"a"。但实现这个一万次修改所消耗的系统资源相差悬殊:

内存消耗:str约为4.73MB,strBui约为0.45MB,相差10倍以上。

时间消耗:str约为24.09秒,strBui约为15.63毫秒,相差1541倍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: