随时记录,想起什么写什么
1.值类型、引用类型
值类型: 包含所有基础类型(int,float,char,bool等)、struct、enum
存储在栈上,作用域结束自动释放
存储的是值本身
普通传参时将值拷贝传入,所以函数内修改参数不会效果原值
引用类型:包含string、class等
存储在堆上,由GC系统管理释放
存储的是实际值的引用
传参时拷贝一份引用传入,所以函数内修改时会改变原值
2.重写、重载
重写:override,用于重写父类方法,需要增加overide关键字,需要与父类中的方法保持相同的返回值、参数列表、函数名
重载:用于对方法进行扩展,可用于实现功能相同但参数不同的方法功能,需与原方法保持相同的函数名与返回值,但参数列表不同
其实对这两个功能本身还是很熟悉的,但是经常会把名字搞混…不知道有没有能加强记忆的方法
个人记法,重写:重新写入,即可覆盖原方法;重载:重复载入,即增加原方法的额外入口
3.for、foreach
使用上最大的区别在于,foreach循环中的元素对象是只读的,不可以在本次循环过程中进行修改、移除等操作
如果需要修改:1.可将需修改的元素保存在另一个集合中,然后在下一次循环中进行修改 2.使用for循环
4.装箱、拆箱
装箱:值类型—>引用类型
拆箱:引用类型—>值类型
装箱过程:
1).在托管堆中为新生成的引用对象分配内存(性能消耗)
2).将之类型的数据拷贝到分配的内存中(性能消耗)
3).返回托管堆中新对象的地址,作为新对象的引用保存在栈中
拆箱过程:
1).获取托管堆中属于值类型部分的地址
2).将引用对象的值拷贝到位于栈上的值类型实例中(性能消耗)
避免装箱的方法:尽量减少将object作为参数或容器类型 1).多使用重载 2).使用泛型
5.String、StringBuilder、StringBuffer
String:不可变(只读),每次修改内容时,实际是返回了一个新的string对象
StringBuilder:可变,可直接进行值修改,线程不安全
StringBuffer: 可变,可直接进行值修改,线程安全
性能优势:StringBuilder > StringBuffer > String
6.dictionary 实现原理
1)使用hash算法将不定长的二进制数转为较短的二进制数,不同的原值可能会映射为相同的结果,即产生冲突
2)对生成的hashCode进行分段,每一段为一个桶,常见算法为将hashcode对桶数取余,得到的结果即为被分配的桶号(此过程可能进一步加深冲突)
3)为解决冲突,常使用拉链法解决,假设a,b被分配到相同的桶中,则将a,b简历一个单链表,桶中只保存链表头
字典的基础结构体
private struct Entry {
public int hashCode; // 除符号位以外的31位hashCode值, 如果该Entry没有被使用,那么为-1
public int next; // 下一个元素的下标索引,如果没有下一个就为-1
public TKey key; // 存放元素的键
public TValue value; // 存放元素的值
}
关键的两个数组:
private int[] buckets; // Hash桶 实际存放为entries的数组下标,
private Entry[] entries; // Entry数组,存放元素
添加元素的流程大概如下:
1).根据key计算对应的hashcode
2).根据hashcode计算对应桶号a
3).将元素封装到Entry结构中,并存入entries数组,记录下标b
4).如果buckets[a]未指向任何,则直接令buckets[a]=b,否则需要将该Entry添加到buckets[a]所代表的链表末尾
字典使用时性能优化
1).使用TryGetValue方法 代替 先ContainKey再取值得方法,后者相当于进行了两次查询
2).避免使用枚举类型作为字典的key,可将枚举转换为int后存入字典。因为字典key进行判断时,如果该类型没有实现IEquatable接口,会使用System.Object.Equals()方法进行比较,进而导致枚举类型判断时产生装箱行为