string,可以很简单,也可以略微复杂一些,完全取决于你对它的理解有多少~
1.引用类型
首先你要明白,它是一个引用类型:(n多例子可以证明它是引用类型的)
string strA = "abc"; string strB = "abc"; bool b=ReferenceEquals(strA, strB);//True
string val = null; //直接就null int a = 12; int b = a; Console.WriteLine(object.ReferenceEquals(a,b));//false string c = "c"; string cc = c; Console.WriteLine(object.ReferenceEquals(c, cc));//true
but 值得注意的一点:
解释如下:
2.不可改变对象
在.NET中String是不可改变对象,一旦创建了一个String对象并为它赋值,它就不可能再改变,也就是你不可能改变一个字符串的值。
比如
string str="abc";
str+="d"
看起来是我们进行了字段串的拼接操作; 但是实际上
这时在堆中其实存在着两个字符串对象,尽管我们只引用了它们中的一个,但是字符串“abc”仍然在内存中驻留
原有的字符串仍然在内存中,不被改变,对实例操作的结果需要在内存中创建新的字符串对象。
这样做的好处:
1)保证原string对象的稳定性。
2)string不会出现线程同步问题。
这样做的坏处:
性能和内存的双重。
为了应对这个缺点,CLR使用了哈希表类型的暂存池
如何去验证了,net 为我们提供了两个方法:IsInterned和Intern
此方法在暂存池中查找 str。 如果已经将 str 放入暂存池中,则返回对此实例的引用;否则返回 null。
看测试代码:
Intern()函数来进行字符串的驻留
string str1 = "abc"; string result = string.IsInterned(str1); Console.WriteLine(result); //结果abc //或者我们这样来测试; string result2 = string.IsInterned("a"); Console.WriteLine(result2);//a //我们只能这样理解了:当写"a"时,暂存池中已经将其加入了,或者说只是 "a" 的申明方式不一样而已,但是它已经驻留了 //string ab = "ab"; //string abcd = ab + "cd"; //这个是拼接出来,驻留词中并没有 //string re = string.IsInterned(abcd); //Console.WriteLine(re);//直接返回空 null string already = "abcd"; //一开始我们就存在该值 string ab = "ab"; string abcd = ab + "cd"; string re = string.IsInterned(abcd); Console.WriteLine(re);//直接abcd
然后我们看看Intern 的基本用法
关于 intern的解释:
看测试代码:
static void Test9() { string strA = "abcdef"; string strB = "abc"; string strC = strB + "def"; var b1 = ReferenceEquals(strA, strC); //False,因为strC是动态构造的,因此这样的字符串不会被添加到暂存池中维护 strC = string.Intern(strC); //否则返回对值为 str 的字符串的新引用 var b2 = ReferenceEquals(strA, strC); //True Console.WriteLine(b2); }
再来看一个测试;
static void Test10() { var ab = "ab"; string aPlusB = "a"+"b"; string a = "a"; string b = "b"; string a_b = a + b; //如果是变量之间的凭借; 返回一个新的引用; Console.WriteLine(object.ReferenceEquals(ab, aPlusB)); //true Console.WriteLine(object.ReferenceEquals(ab, a_b)); //false var xx = string.Intern(a_b); Console.WriteLine(object.ReferenceEquals(ab, xx)); //这样就返回了True //这里附带一篇文章:http://broadcast.oreilly.com/2010/08/understanding-c-stringintern-m.html // https://stackoverflow.com/questions/2506588/c-sharp-string-interning //https://stackoverflow.com/questions/2506588/c-sharp-string-interning StringBuilder sb1 = new StringBuilder("abc"); StringBuilder sb2 = new StringBuilder("abc"); Console.WriteLine(object.ReferenceEquals(sb1,sb2)); //false 完全是不同的两个对象, }
字符串的比较
= =操作首先比较两个字符串的引用,如果引用相同,就直接返回True;如果不同再去比较它们的值
所以如果两个值相同的字符串的比较相对于引用相同的字符串的比较要慢
string a = "1234"; string b = "1234"; string c = "123"; c += "4"; int times = 1000000000; int start, end; ///测试引用相同所用的实际时间 start = Environment.TickCount; for (int i = 0; i < times; i++) { if (a == b) { } } end = Environment.TickCount; Console.WriteLine((end - start)); ///测试引用不同而值相同所用的实际时间 start = Environment.TickCount; for (int i = 0; i < times; i++) { if (a == c) { } } end = Environment.TickCount; Console.WriteLine((end - start));
结果:
明显引用比较快很多;
如果我们全部改成equals呢;
似乎要快一点,但是测试效果并不稳定;
有一点需要明确的是,.NET中==跟Equals()内部机制完全是一样的,==是它的一个重载。
public static bool operator ==(string a, string b) { return string.Equals(a, b); }
public static bool Equals(string a, string b) { if (a == b) { return true; } if ((a != null) && (b != null)) { return a.Equals(b); } return false; }
还是有一个就是我们stringbuilder
这个,一句话总结:
String来做字符串的连接时效率非常低,但并不是所任何情况下都要用StringBuilder,当我们连接很少的字符串时可以用String,但当做大量的或频繁的字符串连接操作时,就一定要用StringBuilder。
ps
上次哥去澳新银行面试的时候,就遇到这这个很基础的问题(string);其实分析大公司的面试题,你会发现,他们很重视基础!
比如上次,thoughwork 的面试题,尼玛,就直接丢算法题给你;窝草;有空我整理一下这两次的面试题;
参考文献:
http://terrylee.cnblogs.com/archive/2005/12/26/304876.html
http://www.cnblogs.com/Benjamin/archive/2013/09/26/3338474.html
http://broadcast.oreilly.com/2010/08/understanding-c-stringintern-m.html