什么是字符池

在程序启动后,CRL会维护一个哈希表,key是字符串,value是这个字符串对应的内存地址,找个哈希表就是字符池。

当一个字符串被静态地创建后,首先查找字符池,如果没有结果,会将其插入字符池,如果查找成功,将该内存地址赋给对应的引用。

代码1

1
2
3
4
5
List<string> L = new List<string>();
while (true)
{
L.Add("aaa");
}

和代码2

1
2
3
4
5
6
List<string> L = new List<string>();
while (true)
{
string s = "aaa";
L.Add(s);
}

这两段代码比起来,谁的效率更高?

答案:代码2

因为在代码1中,每次循环都会创建一个”aaa”对象,虽然这些对象的引用在调用栈中不断地被创建和销毁,但是它们实际上都是在托管堆中动态分配的,而不是通过字符池中的引用访问。

代码2中第一次循环的时候创建了一个”aaa”对象,然后添加到了字符池当中,之后的每次添加,都将访问在字符池中该对象的引用,而不是重新创造一个对象,所以代码2的效率更高。

结论:在进行大量的字符串访问操作的时候,最好使用静态创建对象的方式。如果需要频繁地进行字符串的拼接操作,应该使用StringBuilder。

关于对象的创建方式

C#中所有通过new一个对象的方式来创建对象,都是动态创建。

静态创建对象通常是指使用静态构造函数、静态工厂方法等静态成员来创建对象。

1
2
3
4
//静态创建字符串
string s = "aaa";
//在编译时,字面量的字符串相加会被计算后合并,然后加入字符池
string b = "aaa" + "bbb";

这行代码是静态创建字符串对象。在 C# 中,使用字符串字面量(比如 “aaa”)创建的字符串对象会自动存储在字符串池中,如果池中已经有一个相同的字符串对象,则不会创建新的字符串对象,而是返回现有的字符串对象的引用。因此,这行代码实际上是在字符串池中查找或创建一个字符串对象,并将其引用赋值给变量 s。