深度分析String類型與StringBuilder類型

 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倍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章