在上一編 文章里分享了自定義實現一個高效的String Split方法,接下來同樣分享一下String相關操作的函數Replace.通過反編譯查看String的Replace方法是內置實現無法查看具體 實現源碼,因此無法推斷出String的Replace方法實現如何;不過出於好奇自己手動去實現一個對應的Replace函數,從測試情況來看其效率相對來說比String的Replace方法要好些.
測試描述
為了讓測試更全面所以進行不同內容替換和處理的不同次數添加到測試中.
-
測試用的String數據
Cache-Control:public, max-age=0
Content-Encoding:gzip
Content-Length:9480
Content-Type:text/html; charset=utf-8
Date:Wed, 31 Oct 2012 14:17:06 GMT
Expires:Wed, 31 Oct 2012 14:17:05 GMT
Last-Modified:Wed, 31 Oct 2012 14:17:05 GMT
P3P:CP=NON DSP COR ADM CUR DEV TAI OUR IND NAV PRE STA
P3P:CP=NON DSP COR ADM CUR DEV TAI OUR IND NAV PRE STA
Server:Microsoft-IIS/7.5
Set-Cookie:smark=Branch=default&IsProject=1; domain=.codeplex.com; expires=Fri, 31-Oct-2042 14:17:06 GMT; path=/
Vary:Accept-Encoding
X-AspNet-Version:4.0.30319
X-AspNetMvc-Version:4.0
X-Powered-By:ASP.NET
-
測試相關方法
static void StringReplace(string value,string olddata,string newdata) { for (int i = 0; i < count; i++) { value.Replace(olddata, newdata); } } static void StringExtendReplace(string value, string olddata, string newdata) { for (int i = 0; i < count; i++) { StringExtend.Replace(value,olddata, newdata); } }
-
具體測試代碼
static void Main(string[] args) { value.Replace("1", "010"); StringExtend.Replace(value, "1", "010"); Console.SetOut(new System.IO.StreamWriter("test.txt")); count = 1; TestReplace(value, "1", "010"); count = 10; TestReplace(value, "C", "010"); count = 100; TestReplace(value, "W", "010"); count = 1000; TestReplace(value, "A", "010"); count = 10000; TestReplace(value, "0", "010"); count = 100000; TestReplace(value, ":", "010"); count = 1; TestReplace(value, "ASP.NET", "ASPX"); count = 10; TestReplace(value, "2012", "2048"); count = 100; TestReplace(value, "Wed", "MVC"); count = 1000; TestReplace(value, "Content", "TESTOPQ"); count = 10000; TestReplace(value, "ASP.NET", "ASPX"); count = 100000; TestReplace(value, "OUR", "BBQ"); Console.Out.Flush(); Console.Read(); } static void TestReplace(string value, string olddata, string newdata) { Console.WriteLine("=========[Count:{0}\t ({1} To {2})]===============", count, olddata, newdata); System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Reset(); sw.Start(); StringExtendReplace(value, olddata, newdata); sw.Stop(); Console.WriteLine("StringExtendReplace:\t{0}ms", sw.Elapsed.TotalMilliseconds); sw.Reset(); sw.Start(); StringReplace(value, olddata, newdata); sw.Stop(); Console.WriteLine("StringReplace:\t\t{0}ms", sw.Elapsed.TotalMilliseconds); Console.WriteLine(""); }
-
測試結果
=========[Count:1(1 To 010)]===============
StringExtendReplace:0.1728ms
StringReplace:0.1654ms
=========[Count:10(C To 010)]===============
StringExtendReplace:0.0538ms
StringReplace:0.1002ms
=========[Count:100(W To 010)]===============
StringExtendReplace:0.4561ms
StringReplace:0.9015ms
=========[Count:1000(A To 010)]===============
StringExtendReplace:5.4919ms
StringReplace:11.0184ms
=========[Count:10000(0 To 010)]===============
StringExtendReplace:52.7016ms
StringReplace:96.3369ms
=========[Count:100000(: To 010)]===============
StringExtendReplace:633.8311ms
StringReplace:1025.9258ms
=========[Count:1(ASP.NET To ASPX)]===============
StringExtendReplace:0.0067ms
StringReplace:0.0084ms
=========[Count:10(2012 To 2048)]===============
StringExtendReplace:0.0424ms
StringReplace:0.0889ms
=========[Count:100(Wed To MVC)]===============
StringExtendReplace:0.4074ms
StringReplace:0.8477ms
=========[Count:1000(Content To TESTOPQ)]===============
StringExtendReplace:4.5297ms
StringReplace:7.6571ms
=========[Count:10000(ASP.NET To ASPX)]===============
StringExtendReplace:43.2199ms
StringReplace:82.9951ms
=========[Count:100000(OUR To BBQ)]===============
StringExtendReplace:442.286ms
StringReplace:841.7506ms
方法源代碼
[ThreadStatic] static char[] mTempChars; protected static char[] GetTempData() { if (mTempChars == null) mTempChars = new char[1024 * 64]; return mTempChars; } public static string Replace(string value, string oldData, string newData) { char[] tmpchars = GetTempData(); int newpostion = 0; int oldpostion = 0; int length = value.Length; int oldlength = oldData.Length; int newlength = newData.Length; int index = 0; int copylength = 0; bool eq = false; while (index < value.Length) { eq = true; for (int k = 0; k < oldlength; k++) { if (value[index + k] != oldData[k]) { eq = false; break; } } if (eq) { copylength = index - oldpostion; value.CopyTo(oldpostion, tmpchars, newpostion, copylength); newpostion += copylength; index += oldlength; oldpostion = index; newData.CopyTo(0, tmpchars, newpostion, newlength); newpostion += newlength; } else { index++; } } if (oldpostion < length) { copylength = index - oldpostion; value.CopyTo(oldpostion, tmpchars, newpostion, copylength); newpostion += copylength; } return new string(tmpchars, 0, newpostion); }
總結
通 過[ThreadStatic]來對每個線程分配一組Char[],從而達到最少Char[]的開銷.細心的朋友應該會發現每個線程分析的char[]大 小為64k,換句話說這個方法替換后超過64k則會異常,不過也可以分析需要設置一個最大值.如果有需要也可以給函數添加不區分大小寫替換