“大菜”:源于自己刚踏入猿途混沌时起,自我感觉不是一般的菜,因而得名“大菜”,于自身共勉。
扩展阅读:c#基础系列1---值类型和引用类型
string(严格来说应该是System.String) 类型是我们日常coding中用的最多的类型之一。那什么是String呢?^ ~ ^
String是一个不可变的连续16位的Unicode代码值的集合,它直接派生自System.Object类型。
与之对应的还有一个不常用的安全字符串类型System.Security.SecureString,它会在非托管的内存上分配,以便避开GC的黑手。主要用于安全性特高的场景。[具体可查看msdn这里不展开讨论了。=>msdn查看详情(https://msdn.microsoft.com/zh-cn/library/system.security.securestring(v=vs.100).aspx)
static void Main(string[] args) { string a = "c:\\temp\\1"; string b = @"c:\temp\1"; Console.WriteLine(a); Console.WriteLine(b); Console.Read(); }
String.Intern
String.IsInterned
具体请查看msdn(https://msdn.microsoft.com/zh-cn/library/system.string.isinterned(v=vs.110).aspx) 但是c#编译器默认是不开启字符串留用功能的,因为如果程序大量把字符串留用,应用程序总体性能可能会变得更慢。(微软也是挺纠结的,程序员TMD的更纠结)
string s1 = "hello 大菜"; string s2 = "hello 大菜"; unsafe { fixed(char* p = s1) { Console.WriteLine("字符串地址= 0x{0:x}", (int)p); } fixed (char* p = s2) { Console.WriteLine("字符串地址= 0x{0:x}", (int)p); } }
输出结果:
字符串地址= 0x80002d84 字符串地址= 0x80002d84
可见实例的值只分配了一次,但是有一点需要说明,字符串仅用于编译期能确定值的字符串,也就是常量字符串。如果我的程序修改为:
args = new string[] { "dfasfdsa"}; string s1 = "hello 大菜"+ args[0]; string s2 = "hello 大菜"+args[0]; unsafe{ fixed (char* p = s1) { Console.WriteLine("字符串地址= 0x{0:x}", (int)p); } fixed (char* p = s2) { Console.WriteLine("字符串地址= 0x{0:x}", (int)p); } }
运行结果:
字符串地址= 0x2e3c 字符串地址= 0x2e7c
int count = 100000; Stopwatch sw = new Stopwatch(); sw.Start(); string s = ""; for (int i = 0; i < count; i++) { s += i.ToString(); } sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds);
运行结果:
14221
查看GC的情况
Gc执行的是如此频繁。 性能是可想而知的。接着看一下StringBuilder
int count = 100000; Stopwatch sw = new Stopwatch(); sw.Start(); StringBuilder sb = new StringBuilder();//听说程序员都这样命名StringBuilder for (int i = 0; i < count; i++) { sb.Append(i.ToString()); } sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds);
运行结果:
12
GC情况:
几乎没有GC(可能还未达到触发GC的临界点),如果我合理初始化了StringBuilder 容量,生产环境中结果差距将会更大。 呵呵 ^ ~ ^
这个场景是适合字符串留用的。因为不再需要经过以上的两个步骤,直接哈希表拿到value就可以对比确定了。
基于以上所有知识,那是不是StringBuilder拼接字符串性能永远都高于符号‘+’呢?答案是否定的。
static void Main(string[] args) { int count = 10000000; Stopwatch sw = new Stopwatch(); sw.Start(); string str1 = "str1", str2 = "str2", str3 = "str3"; for (int i = 0; i < count; i++) { string s = str1 + str2 + str3; } sw.Stop(); Console.WriteLine($@"+用时: {sw.ElapsedMilliseconds}" ); sw.Reset(); sw.Start(); for (int i = 0; i < count; i++) { StringBuilder sb = new StringBuilder();//听说程序员都这样命名StringBuilder sb.Append(str1).Append(str2).Append(str3); } sw.Stop(); Console.WriteLine($@"StringBuilder.Append 用时: {sw.ElapsedMilliseconds}"); Console.Read(); }
运行结果:
+用时: 553 StringBuilder.Append 用时: 975
符号‘+’最终会调用String.Concat方法,当同时连接几个字符串时,并不是每连接一个都分配一次内存,而是把几个字符都作为 String.Concat方法的参数,只分配一次内存。所以在拼接的字符串个数比较少的场景下,String.Concat 性能是略高于StringBuilder.Append。string.Format 方法最终调用的是StringBuilder,这里不做展开讨论了,请自行参考其他文档。
所以万事都不是绝对的!!每个事物都有适合自己的场景,我们都需要自己去探索。(程序员太累了)
以上都是非生产环境测试结果,如果错误,请及时指正
请尊重一个猿的辛苦,转载请标明出处 ^ ~ ^ 。部分图片来源于网络,如有侵权请及时联系。让我们一起进步吧
一个不止于IT圈内容的微信公众号,欢迎关注,交流更多的IT知识。不定时会有惊喜奥 ^ ~ ^
本文分享自微信公众号 - 架构师修行之路(jiagoushixiuxing),作者:大菜
原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。
原始发表时间:2018-09-09
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句