1.string与StringBuilder
string 是不可变的,是引用类型继承与Object(值类型继承于ValueType),每次拼接string其实是在托管堆上构造一个新的对象。这样在反复的拼接字符串的时候就会产生大量的垃圾字符串,由GC自动回收,这个时候GC会频繁的回收垃圾字符串,影响性能,所以通常推荐使用StringBuilder来做字符串拼接,
那么string支持字符串拼接的意义何在呢?
这里就得说到字符串池这个概念,当我们创建一个字符串的时候,会先去字符串池中找(哈希表),如果没有找到就会创建一个新的字符串并保存到字符串池中,当创建的字符串已经在字符串池中存在的时候,就会去引用。这样已经创建过的字符串就可以共享,节省空间。因为在程序中,我们会使用大量的字符串,假如我们每次出现的字符串都在内存空间中开辟一块空间来存储,就会在内存中产生大量相同的字符串,也会造成垃圾回收频繁的执行,影响性能。所以string的字符串拼接适合少量的拼接,对于大量的拼接,则推荐使用StringBuilder。
下面说说string的字符串拼接与StringBuilder拼接的简单过程。
string的字符串拼接过程:string a='A'; a+=B;
1.开辟足够大的临时存储空间来保证足够存储字符串A和B。
2.将A复制到临时区的开始处。
3.将B复制到临时存储区的结尾处。
4.释放a的旧有内存。
5.给a分配新内存。
6.将临时存储空间的字符串复制到5中新开辟的内存,并将a的引用指向新内存。
StringBuilder拼接的过程:StringBuilder是链式存储结构,构造函数初始化StringBuilder实例的大小,可存储的最大容量,当前字符串。当新添加的字符串大于当前StringBuilder剩余空间的时候,StringBuilder就会开辟出一块新的空间(大小=原大小*2),新字符串补充满剩余内存空间后,将剩下的字符串放入新开辟的内存空间。引用一下网上的一张图。
2者之间的本质区别在于,string在字符串拼接的时候是创建一个新的对象来保存拼接后的字符串,而StringBuilder则是在原StringBuilder对象上开辟新的内存空间,是操作原对象而非string的创建新对象。
2.值类型一定在栈上吗?
我们都知道值类型是存放在线程栈上面,但其实只有值类型作为临时变量的时候才存放在栈上,作为成员变量,存储在堆中。
1.引用类型的内部变量,即使是值类型,也会在引用类型被实例是一起存放在堆上,方法内部的值类型变量不会初始化,在执行方法的时候放在栈上面。
2.对于值类型的数组,因为数组是引用类型。所以数组内的值类型也在堆上。
3.闭包,lamda表达式。如下:
Action<int> act = a =>{ Console.WriteLine(a); };
C# 成的IL 会添加一个静态的辅助类,闭包内的局部变量 也会成为辅助类的成员变量,因此,这种值类型的局部变量也被分配到堆上。
闭包的陷阱:参考 菩提树下的杨过 :http://www.cnblogs.com/yjmyzz/archive/2009/03/13/1410924.html
using System; using System.Collections.Generic; namespace ConsoleTest { class Program { static void Main(string[] args) { List<Action> ls = new List<Action>(); for (int i = 0; i < 10; i++) { ls.Add(() => Console.WriteLine(i)); } foreach (Action action in ls) { action(); } System.Console.Read(); } } }
结果:一连输出了10行完全相同的"10"(可能并没有按代码编写者的"意图",输出0到9)
看了链接里面其实是编译生成了一个类,类里面有一个变量i和打印i的方法,然后创建一个Action委托集合将方法赋值,最后遍历集合执行,在执行的时候i已经加到10了。所以要通过一个内部变量来避免这种陷阱。。
3.哈希表的实现方法?
哈希表 hashtable ,也称散列表,主要是根据关键码值(key,value)来直接访问数据结构,通过把关键码值映射到表中一个位置来访问记录。
它的实现方法是:把key通过一个固定的算法函数(除余法,数字选择法)来转换成一个整数字符,然后将该整数除以一个数字进行取余,将余数作为数组的下标,将value存储在该余数的数组下标的空间内。当使用哈希表查询的时候,再次将key通过相同函数转换整数取余得到下标,定位到对应的数组控件获取Value值。(通过余数分配不同的数组空间,查询的时候通过余数找对应的数组空间,这样就避免了全表扫描,节约时间。)
a) 除余法:
选择一个适当的正整数 p ,令 h(k ) = k mod p ,这里, p 如果选取的是比较大的素数,效果比较好。而且此法非常容易实现,因此是最常用的方法。
b) 数字选择法:
如果关键字的位数比较多,超过长整型范围而无法直接运算,可以选择其中数字分布比较均匀的若干位,所组成的新的值作为关键字或者直接作为函数值。