集合操作总结日志

1.集合:  命名空间System.Collection

a) 集合常用操作: 增删改查 遍历 来自于IList接口

b) 就是一个能装一堆东西的容器. 而变量是一个能装一件东西的容器

i. ArrayList arr=new ArrayList(); 可变长度数组,使用类似于数组, 在添加第一个 元素 的时候 ,给数组扩容默认4个长度,再以后每一次添加元素超过数组长度时,就将实际添加数量赋给数组的长度.(比如数组长度现在是4,你要添加6个元素,这样一共为10个元素,数组这是不是扩容2次变为16的长度,而是只扩容一次,然后将元素的实际长度赋给数组的长度)

1. 属性:    Capacity:总容量  Count:数量

2. AddRange(ICollection );实现原理: 先扩容,然后调用 Array.CopyTo();方法 存入新数据.  

3. arr.Capacity=200; 创建一个200大小的数组 然后用新数组 替换 旧数组.

4. Remove() 是根据引用来删除元素的. Remove()中查找删除对象时的比较方法是Object里 默认的的Equals,比较方式是比较的 地址. 可以用户自定义比较类型 重写Remove()方法.

5. ToArray(); 直接获得ArrayList 中的数组 返回一个Object[]数组

6. Hashtable(key,value): Hashtable里的键不可以重复, 存储键的时候通过hashcode 查找存储地址,从而找到对应的值.

a) Hash算法:能够快速定位对象. 按照Hash码取值

b) ()当我们向Hashtable中Add元素时,元素储存在Hashtable的数组里的,下标是根据添加key的hash值算出来的(但因为hash值 取余 arr.Length(数组长度),所以肯定不会超过当前数组长度) !!!       Hashtable 储存元素时,是根据key的Hash值(hash值是通过GetHashCode()算出来的) %(取余)  数组长度 求出的一个下标值

 1.获得key的哈希值.

2.用哈希值取余数组长度 获得一个下标index

3.创建一个bucket对象,并设置3个成员值,添  加到index位置

4.注意:每个对象算出的Hashcode并不是唯一  的,有可能出现多个对象的Hashcode相同.

a) 解决机制: 1.再次hash一次. 2.桶装模式,将两个相同hashcode的对象装入同一位置.

5.当新增时,Hashtable里的容器数组已经满了,则以数组两倍的长度扩容.

(取) :  当我们从Hashtable取元素时(根据key来取),会根据key的hash值算出 对应要取元素的下标,并且比较元素里的key和当前要找的key参数的hash值是否相等,同时还要比较两个key的引用是都一致,如果都满足,则确定找到要取的元素

 

总结: 存 : 会根据key的 hash值 算出一个下标, 然后存

      取 : 会根据key的 hash值,算出一个下标, 然后取

取值得时候不需要遍历, 算出来下标直接取.

哈希表Hashtable实现原理:Hashtable中的实际数据都存储在一个内部的buket[]数组中,当用户希望取得Hashtable值得时候,Hashtable进行如下处理:

1.为了保证取值范围保存在 0--Buket.Length之间, 首先哈希算法根据键key 与数组长度Length 进行取模运算,算的实际数据的位置为: (f(K)=HashOf(K) % Array.Length)  至于这个哈希函数Hash(f)怎么算出来的,简单说,可以取关键字的AscII码,根据一定规则运算得到.

2.如果发现多个Key值得哈希值重复,如果发生多个 K 值的哈希值重复, 即 f(K1) = f(K2), 而 f(K1) 位置已经有数据占用了, Hashtable 采用的是 "开放定址法" 处理冲突, 具体行为是把 HashOf(K2) % Array.Length 改为 (HashOf(K2) + d(K2)) % Array.Length , 得出另外一个位置来存储关键字 K2 所对应的数据, d 是一个增量函数. 如果仍然冲突, 则再次进行增量, 依此循环直到找到一个 Array 中的空位为止. 将来查找 K2 的时候先搜索 HashOf(K2) 一档, 发现不是 K2, 那么增量 d(K2) 继续搜索, 直到找到为止. 连续冲突次数越多, 搜索次数也越多, 效率越低.

3.当插入的数据达到Hashtable容量的上限时,对 内部buket数组进行扩容(重新new一个更大的数组,然后把数据copy过去,最后将新数组的地址引用再付给旧数组)

4.随着插入的数据项逐渐增多, Hashtable 内部数组剩余的空位也越来越少下一次冲突的可能性也越来越多严重影响效率因此不能等到数组全部塞满后才进行扩容处理在 .NET 当插入数据个数和数组容量之比为 0.72 就开始扩容这个 0.72 称为装填因子 - Load Factor. 这是一个要求苛刻的数字某些时刻将装填因子增减 0.01, 可能你的 Hashtable 存取效率就提高或降低了 50%, 其原因是装填因子决定 Array.Length, Array.Length 影响 f(K) 的冲突几率进而影响了性能. 0.72 是 Microsoft 经过长期实验得出的一个比较平衡的值. (取什么值合适和 f(K) 的算法也有关, 0.72 不一定适合其他结构的哈希表)

5.Hashtable 的初始容量 Array.Length 至少为 11, 再次扩容的容量至少为 "不小于 倍于当前容量的一个质数". 这里举一个例子方便大家看看 Hashtable 是多么浪费空间.

假设以默认方式初始化一个 Hashtable, 依次插入 个值由于 8 / 0.72 > 11, 因此 Hashtable 自动扩容新的容量为不小于 11 * 2 的质数即 23. 所以实际仅有 个人吃饭却不得不安排一桌 23 个座儿的酒席十分奢侈避免如此铺张的途径是在初始化 Hashtable 时用带参构造方式直接指定 capacity 为 17, 但即便这样仍浪费了 个空间.

有心的读者经过计算可能会问为什么不是指定初始容量为 13, 13 是质数啊, 13 * 0.72 > 8 确实理想情况是这样但实际上由于动态计算并判断一个数是否质数需要大量时间故 .NET Hashtable 中的 capacity 值是内部预设的一个数列只能为 3, 7, 11, 17, 23... 所以十分遗憾. (只有当 Array.Length > 0x6DDA89 时动态计算扩容容量正常情况下我们不会存如此多的数据进去)

.NET 的 Hashtable 就是以这种方式来减少冲突以牺牲空间为代价换取读写速度假设你在实际开发中对内存空间要求很敏感譬如开发 ASP.NET 超大型 B/S 网站时就十分有必要检讨使用 Hashtable 的场景需求有的时候能否换个方式采取自定义 struct, 或者数组来高效实现呢?

 

 

 

posted @ 2012-07-01 08:11  zxp19880910  阅读(151)  评论(0编辑  收藏  举报