String interning 和 String.Empty

在code review时候看到了一篇不错的blog,边翻译边做了一些自己的理解。

-------------------------------------------------------------------------------------------------------------------

 

问题的代码片断如下:

object obj = "Int32";
string str1 = "Int32";
string str2 = typeof(int).Name;
Console.WriteLine(obj == str1);    //   #1 true
Console.WriteLine(str1 == str2);   //   #2 true
Console.WriteLine(obj == str2);    //  #3 false !?


我们已知的定理是, 如果A=B,B=C,那么A=C,即等量的传递性。但是如现在的代码所示,这条定理则变得有所不同。现在的情况是我们混淆了2种不同类型的"相等": 引用相等和值相等。通常,对象作引用比较,所以在#1和#3中其实在测试的是2个对象引用是否指向同一个对象;#2是在检查2个对象是否具有相同的内容(不管它们是否是同一个对象), 当然这全都归功于string对"== " 的重写。事实上,编译器会在这种场合上给出警告,例如“这可能是无意的引用比较,把左值转化为string”。

 

这里需要多做一些解释,在.Net中你能够拥有2个不同的对象却具有相同的内容,当你以string类型来做比较字符串对象时,它们是相等的,但当你以objects形式比较时,则不相等。这就是解释了为什么#2是相等的,因为它是一个值比较;并且,我们看到#3返回的是false,因为它是引用的比较,但是这并没有解释为什么#1和#3本质上是不同的比较。

 

其实,在这里这是一个次优化的问题(small optimization - 我姑且是这么称呼的,对理解上下文问题不大)。如果你有2个相同字面值在同一块编译单位中,然后生成的代码保证只有一个string对象是由CLR为所有在这个程序集中拥有那个字面值的实例构建的。这个优化就是所谓的“string interning”。详细的说明可以查阅MSDN:http://msdn.microsoft.com/en-us/library/system.string.intern.aspx

 

(其实的事例代码如下, 其实我感觉到用object.ReferenceEquals作引用的比较会感觉更直观些

 

string s1 = "MyTest";
string s2 = new StringBuilder().Append("My").Append("Test").ToString();
string s3 = String.Intern(s2);
Console.WriteLine((Object)s2==(Object)s1); // Different references.
Console.WriteLine((Object)s3==(Object)s1); // The same reference.

)

---------------------------------------------------------------------------------------------------------------------------------------------------------


String.Empty不是常量,而是一个在另一个程序集的只读域。因此它是非interned的。这解释了为什么#1返回true:2个字面值事实上转变为了 一个string对象。同时也解释了为什么#3返回false,字面值和计算值转变为了2个不同的对象。


在看以下代码片断:

 

object obj = "";
string str1 = "";
string str2 = String.Empty;
Console.WriteLine(obj == str1); // #4 true
Console.WriteLine(str1 == str2); // #5 true
Console.WriteLine(obj == str2); // #6 sometimes true, sometimes false?!


某些版本的.Net CLR在运行时自动intern空字符串,但有些则不是。为什么?难道我们不是时在运行时对所有的string都作interning的优化? 为什么不是把所有值相等的trings都转变为引用相等的string?

 

这里有一个被称为TANSTAAFL得原则。Interning有2个优势:它减低了内存的消耗,降低了比较2个string的时间 (因为如果所有的
string都在运行时被intern了,那所有的string比较都可以成为不太昂贵的引用比较)。但是这样的好处会有一个小代价:在分配一个新的 string之前,需要在内存中查找所有的string字面值,看有没有已经匹配的string。在当前的优化方案中,这个动作在运行时被强加上去了,并且在这可能成为比较大的时间消耗(分配string时)。


为了尽量减低时间上的消耗,你不得不在内存中作一个hash table来存放所有的string, 这就意味着或者频繁的计算hash,可能又是比较昂贵
的,或者把hash存放在某个地方。如果用后者方案,我们会突增不少内存的负担。 所以,现在的优化方案应是在正常情况下进行,即大部string是不相等的,这似乎是个不太好的妥协,通常情况下我们想为可能情况优化。

 

 

简单说,通常情况下我们没有必要intern所有的string。然而,有些特殊的情况则需要。比如,如果你用的是C#写的的编译器,它可能会在运行时产生的许多string是一样的,MS的C#编译器是用C++开发的,其中自己定义了interning来针对string做低消耗的比较。

 

另外,有关Java中string的interning的问题,可以参考http://blog.csdn.net/frankroast/archive/2005/04/05/336911.aspx,个人觉得写得非常到位 =)

 

 

 

 

J

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值