OC类和对象之四hash结构
1,简介
hash是一种将关键字data的key值进行散列存储的技术,它可以根据关键字的key值得到关键字的实际存储位置,它查找的效率为O(1)级别,现实中根据冲突的情况可能会是1点多的情况。
2,举例
例如现在有100个关键字结构体,它们的key的取值范围为1-10000,如果内存够用的话,可以直接分配10000个空间,根据关键字的key值直接放入到对应的空间里面就行。但是一般情况下是不太可能有全部的空间的,例如对于指针类型的数据进行hash,它们的取值范围为全部的4个或者8个字节数据,这种情况就不能直接以关键字的key值作为实际的存储地址了,但是我们可以进行一次hash处理,将大范围的数据经过函数处理成小范围的数据。例如对于4字节指针,它的取值范围为0~2^32-1, 指针本身的取值范围很大,如果预估计key值在100个以内,那么对于指针p,可以采用hash(key) = (int)p % 101,这样上亿的范围的key经过求余就被处理成了0-100的范围。毫无疑问,不同的key可能会有相同的hash值,这就是常说的冲突。下面提供一种常见的解决冲突的方法-链地址吧。它通过相同的hash值的关键字放到一个链表里。对于上述情况首先是一个0-100的数组,数组元素就是一个链表。如果需要查找一个指针p,首先进行(int)p%101得到它的hash值,然后以hash值作为数组索引,找到一个链表;然后在链表中进行从前向后的遍历即可。
3,Object-C库
苹果object-c源代码的hash结构就是采用这种各大数据结构体教材中都会讲的类似于链地址的方式解决冲突的。不过它的链地址不是一个链表,而是一个数组结构,而且当出现冲突的时候,它通过释放原来的数组结构,并进行分配大一个数组结构来存放新的元素的。它的劣势很明显,如果冲突比较多会频繁的alloc和free,它的优势是不用二级指针的指向来找到元素,为了进一步优化,它对于一个和两个元素采用了oneOrMany的union结构,这样对一个的元素进行hashGet会减少一次二级指针的获取操作,这应该是预估了冲突一般情况下都很少发生才这样做的。整个Hash结构是一个大数组,数组的基地址是buckets。每一个bucket可以看成是一个小数组,它存储着具有相同hash值的冲突列表。
3.1,整体Hash结构
首先看一下整个hash结构体,我已经在代码中加入了个人注释:
typedef struct {
const NXHashTablePrototype *prototype OBJC_HASH_AVAILABILITY; //hash结构所使用的hash,以及比较函数的函数表。
unsigned count OBJC_HASH_AVAILABILITY; //hash结构已经有的关键字个数
unsigned nbBuckets OBJC_HASH_AVAILABILITY; //此hash表的容量大小,随着count