1.驻留机制
从老师给的资料 http://www.cnblogs.com/solan/archive/2012/08/03/CSharp07.html大概了解了C#底层对字符串的处理。
字符串操作是最基本的操作之一,随便给一篇文章,里面的字符串就有很多,处理这样大量的数据,无论是空间还是时间我们都应该优化,在C#中,实现这个功能的就是驻留机制。驻留机制顾名思义,就是字符串常驻在某个地方,这个地方就是内存空间。
我们在创建一个string的对象的时候,正常的话对于每一个string对象都申请一个空间存放这个串,这个对象一旦创建就无法修改了,只能改变这个对象的引用,来改变这个对象对应的串,那么原来的那个空间就会被回收,这样看来,对象的不可修改性我们还是可以接受的。不过,我们假设我们有很多相同的串,那么他们每一个都会占用一定的内存空间,这样我们可以想到一个优化的想法,就是把所有的一样的都指向一个内存空间可以这么来看这个问题的优化
string str1 ="xym";
string str2 = "xym";
string str3 = "xym";
我们想到的优化就是str2 = str1; str3= str1;这样str2和str3的空间就被回收了,全部指向str1的那部分空间,正是因为不可修改性,对str1,str2,str3每个单独操作不会有任何冲突。这其实就是驻留机制的作用了,让所有相同的串共用同一个内存空间,大大的节约了空间,和回收机制的负担,资料中说的是在创建对象的时候,会先查询这个串是否在在他内部的哈希表中,如果在,就指向那部分内存,不在则插入。
这里http://www.soaspx.com/dotnet/csharp/csharp_20110227_7258.html有对驻留机制特性很好的总结
一、具有相同字符序列的String对象不会重复创建
二、字符串驻留机制同样于string literal + string literal的运算
三、字符串驻留机智不适合variable + string literal形式
四、调用string.Intern可以对运算结果进行强制驻留
五、驻留的字符串不能被GC回收
六、字符串驻留是基于整个进程的
2.字符串声明
string Mystring;
string Mystring1 =string.Empty;
string Mystring2 ="";
下面我们来讨论一下这三者的区别
debug发现这三者声明后赋值是不一样的,其中Mystring1和Mystring2是一样的,而因为Mystring没有给初始值,所以是默认值null,注意在C#里面有很好的保护机制,你使用未赋值的变量或对象的时候会报错误,所以我们必须给Mystring赋值才可以使用,这里我有一个问题如果我们开始的时候string Mystring = null;这样是可以用的,那么编译器是怎么判断我们有没有初始化的, 我觉得是判断那个等号,网上没有找到有关资料。
现在看看Mystring1 和 Mystring2的引用是不是一样,根据驻留机制他们应该是一样的。
static void Main(string[] args)
{
string Mystring ;
string Mystring1 = string.Empty;
string Mystring2 = "";
Console.WriteLine(object.ReferenceEquals(Mystring1, Mystring2));
}
答案是true
那么这和我们前面的驻留空间也就不谋而合了,表示的都是空串,都是相同的引用,那么我们可以认为Mystring1和Mystring2是等价的,所以这两种声明也是等价的。
3.对比试验
据说判断一个字符串是否为空,采用if(0 == mystring.length) 的速度是最快的,比if( "" == mystring) 要快,下面我们来做一个对比试验。
我设计的试验对于10000,100000,1000000,10000000,100000000次判断测试速度,这里mystring = “”,每个试验10次,取平均值
下面是本机跑if( "" == Mystring ) 的时间
循环10000次:0.20002ms
循环100000次:0.90005ms
循环1000000次:7.30042ms
循环10000000次:71.90411ms
循环100000000次:714.44086ms
请按任意键继续. . .
下图是跑if (0 == Mystring.Length)的时间
循环10000次:0.20001ms
循环100000次:0.90005ms
循环1000000次:8.30048ms
循环10000000次:84.10482ms
循环100000000次:826.44727ms
请按任意键继续. . .
我反复运行了多次,发现if("" == Mystring) 比 if (0 == Mystring.Length)要快,注意我这里是Mystring == "",如果我们让Mystring ="123456789", if (0 == Mystring.Length)时间会不会 更长,length函数这个算法应该是O(n),我们猜测时间会更长,那么我们再次试验一下
下图是跑Mystring ="123456789" if (0 == Mystring.Length)的时间
循环10000次:0.20001ms
循环100000次:1.00006ms
循环1000000次:8.30047ms
循环10000000次:82.90474ms
循环100000000次:819.04684ms
请按任意键继续. . .
时间有所增加,那么我们来看看if("" == Mystring)
循环10000次:0.30002ms
循环100000次:1.30008ms
循环1000000次:9.30052ms
循环10000000次:95.00543ms
循环100000000次:923.65283ms
请按任意键继续. . .
由试验结果发现,当mystring != ""时, if (0 == Mystring.Length)比if("" == Mystring) 要快,这里我们可以思考一下为什么会这样,其实字符串比较是否相等,我们首先比较两个字符串长度是否相等,然后再逐个比较,那么当mystring = ""的时候,显然if("" == mystring) 先计算两个字符串的长度,然后在逐个比较,这样看来,if("" == mystring)要计算两次字符串的长度,但是如果这两个字符串指向相同内存,那么显然比较的时候可以先判断两个的引用是不是一致的,一致的话肯定是相同的,
那么我们可以再做一个实验,如果mystring = "123",那么if("123" == mystring) 和if(3 == mystring.length) 这两个哪个快点。
首先看看if("123" == mystring)
循环10000次:0.20001ms
循环100000次:0.90005ms
循环1000000次:7.20041ms
循环10000000次:70.10401ms
循环100000000次:711.44069ms
请按任意键继续. . .
这个和上面时间相近
看看if(3 == mystring.length)
循环10000次:0.20001ms
循环100000次:0.90005ms
循环1000000次:8.50048ms
循环10000000次:85.7049ms
循环100000000次:841.64814ms
请按任意键继续. . .
猜想完全正确,在字符串比较的时候应该是先比较引用是否相同,然后再比较长度,然后再逐个比较。这样对于上面的比较试验的结果,就可以解释了,也证明了驻留机制。
结论,在mystring != "" 的时候if(0 == mystring.length) 的速度是最快的,当 mystring == "" 的时候, if (0 == Mystring.Length)速度是最快的。