与哈希表有关

本质

  • 哈希表本质其实是数组,它是通过关键码值而直接进行访问的数据结构。将key映射到数组中所对应位置,已便于对数组中的值进行快速查找,这个映射函数就是散列函数,这个存放数组即为散列表;
  • 哈希表存放时其实是通过哈希函数将key转为整型数,再通过数组的长度进行取余,得到key在数组中所对应的位置address,将value值存放在address对应的位置处。
  • 哈希表读取时是通过哈希函数将key转为整型数,再通过数组的长度进行取余得到所在数组中的位置,并按照该位置去数组中取值value,这样可以快速定位到当前key在哈希表中的值;

哈希函数

  • 生成构造哈希函数的方法:
  1. 直接定址法:取关键字或者关键字的某个线性函数为Hash地址,即address(key)=a*key+b;假设学生的学号从2000开始,最大为4000,则可以将address(key) = key - 2000作为Hash地址;
  2. 平方取中法对关键字进行平方运算,然后取结果的中间几位作为Hash地址。假如有以下关键字序列{421,423,436},平方之后的结果为{177241,178929,190096},那么可以取中间的两位数{72,89,00}作为Hash地址;
  3. 除留取余法:如果知道Hash表的最大长度为m,可以取不大于m的最大质数p或某个固定数p,然后对关键字key进行取余运算得到在数组中的对应位置address,address(key)=key%p。

冲突

  • 当我们以固定长度来进行存放数据时,有时候可能会存在不同的key计算出来的address是相同的而导致哈希表冲突,所以此时也要求我们要对存在的冲突进行解决,以下几种方法供我们参考解决冲突。而且当哈希表存储的容量接近满时,需要我们对哈希表进行扩容,重新编排哈希表中数据的位置。
  1. 开放定址法:当一个关键字和另一个关键字发生冲突时,使用某种探测技术在Hash表中形成一个探测序列,然后沿着这个探测序列依次查找下去,当碰到一个空的单元时,则插入其中。比较常用的探测方法有线性探测法,比如有一组关键字{12,13,25,23,38,34,6,84,91},Hash表长为14,Hash函数为address(key)=key%11,当插入12,13,25时可以直接插入,而当插入23时,地址1被占用了,因此沿着地址1依次往下探测(探测步长可以根据情况而定),直到探测到地址4,发现为空,则将23插入其中。
  2. 链地址法:采用数组和链表相结合的办法,将Hash地址相同的记录存储在一张线性表中,而每张表的表头的序号即为计算得到的Hash地址。如上述例子中,采用链地址法形成的Hash表存储表示如下

虽然能够采用一些办法去减少冲突,但是冲突是无法完全避免的。因此需要根据实际情况选取解决冲突的办法。

 

字典

  • 原理:NSDictionary的实现也是哈希表,通过开放定址法将value存放在key所对应的哈希表中位置处。当NSDictionary存放大量数据时,哈希表需要扩容,导致哈希表中数据需要根据新的固定长度重新计算位置,导致执行效率降低,消耗cpu性能!
  • 缺点:当我们对NSDictionary进行setObject..forKey时,key要求遵循NSCopying,毕竟在NSDictionary内部会对key进行复制copy操作,而且也会对Object进行强引用的操作,导致对象不能及时释放!
  • 建议:不建议字典的key用对象来充当,虽然说可以遵循NSCopying实现copyWithZone方法,但是还是尽量不要这样操作,容易出现一些意想不到的问题,例如对象可能为nil;
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
{
    YRGou* gou = [[YRGou alloc] init]; // YRGou为自定义的class
    [dict setObject:gou forKey:@"Gou"];
    NSLog(@"%@", dict);
}
NSLog(@"%@", dict);

// 打印结果
// {
//    Gou = "<YRGou: 0x60000225c240>";
//}

// {
//    Gou = "<YRGou: 0x60000225c240>";
//}

解析:当我们gou对象超过作用域后需要被释放,但是却仍然在字典dict中被引用着!

NSMapTable

  • 强大作用
  1. NSMapTable是类似于字典可以进行设置Object与对应的key,并且可以对Object及key的属性类型进行设置!
  2. 不仅可以设置Key-value映射,还可以Object-Object映射!
  • NSPointerFunctionsOptions
  1. NSPointerFunctionsStrongMemory: 强引用存储对象
  2. NSPointerFunctionsWeakMemory: 弱引用存储对象
  3. NSPointerFunctionsCopyIn:copy存储对象
// 案例1
YRGou* gou = [YRGou new];
gou.name = @"li";
NSMapTable* maps = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory];
{
    YRGou* gou2 = [YRGou new];
    gou2.name = @"li2";
    [maps setObject:gou forKey:gou2];
    NSLog(@"%@", maps);
}
NSLog(@"%@", maps);

/** 结果:
{
  <YRGou: 0x600001bce780> -> <YRGou: 0x600001bce830>
}
{
}
**/

// 案例2
YRGou* gou = [YRGou new];
gou.name = @"li";
NSMapTable* maps = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsStrongMemory];
{
    YRGou* gou2 = [YRGou new];
    gou2.name = @"li2";
    [maps setObject:gou forKey:gou2];
    NSLog(@"%@", maps);
}
NSLog(@"%@", maps);

/** 结果:
{
  <YRGou: 0x600002dbc430> -> <YRGou: 0x600002dbc3f0>
}
{
  <YRGou: 0x600002dbc430> -> <YRGou: 0x600002dbc3f0>
}
**/
  • 解析:NSPointerFunctionsOptions设置了weak跟strong的区别,当设置weak时则不强引用着对象,在超出作用域后对象将被回收;相反设置为strong则使强引用地对象能继续存在,不被系统回收!

NSMapTable与NSDictionary区别

  1. NSDcitionary有一个可变类型NSMutableDictionary,NSMapTable没有可变类型,它本身就是可变的;
  2. NSDcitionary/NSMutableDictionary中对于key和value的内存管理方法唯一,即对key进行copy,对value进行强引用,而NSMapTable没有限制;
  3. NSDcitionary中对key值进行copy,不可改变,通常用字符串作为key值,只是key->object的映射,而NSMapTable的key是可变的对象,既可以实现key->object的映射,又可以实现object->object的映射。

 

SDWebImage学习笔记之NSMapTable

NSDictionary和NSMutableArray底层原理

NSArray,NSDictionary,NSSet当中的算法知识

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值