NSDictionary实现原理-ios哈希hash和isEqual

 
OC中自定义类的NSCopying实现的注意事项(isEqual & hash实现)
 
iOS开发 之 不要告诉我你真的懂isEqual与hash!

NSDictionary(字典)是使用 hash表来实现key和value之间的映射和存储的, hash函数设计的好坏影响着数据的查找访问效率。数据在hash表中分布的越均匀,其访问效率越高。而在Objective-C中,通常都是利用NSString 来作为键值,其内部使用的hash函数也是通过使用 NSString对象作为键值来保证数据的各个节点在hash表中均匀分布。

见NSDictionary中最常用的一个方法原型:

[objc] view plain copy

 
  1. - (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;
从这个方法中可以知道, 要作为 Key 值,必须遵循 NSCopying 协议。这是因为在NSDictionary内部,会对 aKey 对象 copy 一份新的。而  anObject 对象在其内部是作为强引用(retain或strong)。
 

既然知道了作为 key 值,必须遵循 NSCopying 协议,说明除了 NSString 对象之外,我们还可以使用其他类型对象来作为 NSDictionary 的 key值。不过这还不够,作为 key 值,该类型还必须继承于 NSObject 并且要重载一下两个方法:

[objc] view plain copy

 
  1. - (NSUInteger)hash;
 
[objc] view plain copy

 
  1. - (BOOL)isEqual:(id)object;
 
 
其中,hash 方法是用来计算该对象的 hash 值,最终的 hash 值决定了该对象在 hash 表中存储的位置。我们重写hash方法是因为每向NSDictionary和NSSet中存入一个key-value,字典会先利用即将插入的key的hash和字典中已经存在的所有的key.hash进行比较,最终来决定是新增一个key,还是覆盖原有的key。 但是仅仅使用key.hash比较,有时会出现2个对象hash相同的情况,这时候就需要调用isEqual 方法来最终裁定,2个key对象是否相同。
 
 
 

在OC中,如果自定义类,则要考虑赋值、持久化保存、保存到其它容器中等各种情况的对象复制和比较,下面是一个比较全面的自定义例子,在此仅作记录:

自定义类:

KeyValuePairs.h:

[objc] view plain copy

 
  1. #import <Foundation/Foundation.h>
  2. @interface KeyValuePairs: NSObject <NSCopying>
  3. @property (nonatomic,strong)NSString *identifier;
  4. @property (nonatomic,strong)NSString *name;
  5. @end
KeyValuePairs.m:
[objc] view plain copy

 
  1. #import "KeyValuePairs.h"
  2. @implementation KeyValuePairs
  3. - (id)copyWithZone:(NSZone *)zone
  4. {
  5. KeyValuePairs *kvp = [[[self class] allocWithZone:zone] init];
  6. kvp.identifier = self.identifier;
  7. kvp.name = self.name;
  8. return kvp;
  9. }
  10. - (BOOL)isEqualToKeyValuePairs:(KeyValuePairs *)kvp{
  11. if (!kvp) {
  12. return NO;
  13. }
  14. BOOL haveEqualName = (!self.name && !kvp.name) || [self.name isEqualToString:kvp.name];
  15. BOOL haveEqualIdentifier = (!self.identifier && !kvp.identifier) || [self.identifier isEqualToString:kvp.identifier];
  16. return haveEqualName && haveEqualIdentifier;
  17. }
  18. #pragma mark -NSObject
  19. -(BOOL)isEqual:(id)object{
  20. if (self == object) {
  21. return YES;
  22. }
  23. if (![object isKindOfClass:[KeyValuePairs class]]) {
  24. return NO;
  25. }
  26. return [self isEqualToKeyValuePairs:(KeyValuePairs *)object];
  27. }
  28. - (NSUInteger)hash {
  29. return [self.name hash] ^ [self.identifier hash];
  30. }
  31. @end

测试:

[objc] view plain copy

 
  1. NSMutableDictionary *namesWillUpdateDic = [[NSMutableDictionary alloc] init];
  2. NSMutableArray *names = [[NSMutableArray alloc] init];
  3. for (int i = 0; i<1000; i++) {
  4. NSString *name = [NSString stringWithFormat:@"%d_zhangsan",i];
  5. NSString *identifier = [NSString stringWithFormat:@"%d_identifier",i];
  6. NSString *strObj = [NSString stringWithFormat:@"%d_strObj",i];
  7. KeyValuePairs *kvp = [[KeyValuePairs alloc] init];
  8. kvp.identifier = identifier;
  9. kvp.name = name;
  10. [namesWillUpdateDic setObject:strObj forKey:kvp];
  11. [names addObject:kvp];
  12. }
  13. for (int j = 0; j<1000; j++) {
  14. int index = arc4random()%1000;
  15. KeyValuePairs *kvp = [names objectAtIndex:index];
  16. NSString *strObj = [namesWillUpdateDic objectForKey:kvp];
  17. NSString *msg = [NSString stringWithFormat:@"index:%d,identifier:%@,email:%@,strObj:%@",index,kvp.identifier,kvp.name,strObj];
  18. NSLog(@"%@",msg);
  19. }
 
 
 
注意:
1、自定义类为什么一定要实现NSCopying协议呢?这是因为通过key-value把2个对象加入到字典中,字典会对key进行copy一份的操作,而对value对象进行retain操作,如果自定义的类不实现copy协议,那么就不能作为字典的key对象使用。
2、如果自定义类不重写isEqual则默认使用内存地址比较两个对象,可能会出现意想不到的结果
3、isEqual和hash方法要同时重写,否则isEqual方法判断将不正确
 
 
 

NSDictionary实现原理-ios哈希hash和isEqual的更多相关文章

  1. iOS判断对象相等 重写isEqual、isEqualToClass、hash

    相等的概念是探究哲学和数学的核心,并且对道德.公正和公共政策的问题有着深远的影响. 从一个经验主义者的角度来看,两个物体不能依据一些观测标准中分辨出来,它们就是相等的.在人文方面,平等主义者认为相等意 ...

  2. redis 哈希(hash)函数

    哈希(hash)函数 hSet 命令/方法/函数 Adds a value to the hash stored at key. If this value is already in the has ...

  3. redist命令操作(二)--哈希Hash,列表List

    1.Redis 哈希(Hash) 参考菜鸟教程:http://www.runoob.com/redis/redis-hashes.html Redis hash 是一个string类型的field和v ...

  4. Redis中的哈希(Hash)

    Redis 哈希(Hash) Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象. Redis 中每个 hash 可以存储 232 - 1 键值 ...

  5. 大话Java中的哈希(hash)结构(一)

    o( ̄▽ ̄)d 小伙伴们在上网或者搞程序设计的时候,总是会听到关于“哈希(hash)”的一些东西.比如哈希算法.哈希表等等的名词,那么什么是hash呢? 一.相关概念 1.hash算法:一类特殊的算法 ...

  6. Python操作redis系列以 哈希(Hash)命令详解(四)

    # -*- coding: utf-8 -*- import redis #这个redis不能用,请根据自己的需要修改 r =redis.Redis(host=") 1. Hset 命令用于 ...

  7. Redis 命令,键(key),字符串(String),哈希(Hash),列表(List),集合(Set)(二)

      Redis 命令 Redis 命令用于在 redis 服务上执行操作. 要在 redis 服务上执行命令需要一个 redis 客户端.Redis 客户端在我们之前下载的的 redis 的安装包中. ...

  8. 哈希--Hash,“散列”/“哈希”

    哈希 Hash,翻译“散列”,音译为“哈希”,把任意长度的输入,通过散列算法,变换成固定长度的输出,该输出就是散列值.这种转换是一种压缩映射,也就是散列值的空间通常远小于输入的空间,不同的输入可能会散 ...

  9. 区块链 - 哈希(Hash)

    章节 区块链 – 介绍 区块链 – 发展历史 区块链 – 比特币 区块链 – 应用发展阶段 区块链 – 非对称加密 区块链 – 哈希(Hash) 区块链 – 挖矿 区块链 – 链接区块 区块链 – 工 ...

随机推荐

  1. js-ES6学习笔记-module(1)

    1.在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种.前者用于服务器,后者用于浏览器.ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可 ...

  2. 【读书笔记】iOS-网络-HTTP-URL百分号编码

    代码: - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, ty ...

  3. Mac下使用VScode进行C/C++开发

    1.安装 从VScode官网下载Mac系统适用的VScode安装包,下载完成后,将zip安装包解压到桌面即可. 2.插件安装 实现 C/Cpp 代码自动补全,函数跳转. 打开VScode后,按下组合键 ...

  4. Android应用程序进程启动过程(前篇)

    在此前我讲过Android系统的启动流程,系统启动后,我们就比较关心应用程序是如何启动的,这一篇我们来一起学习Android7.0 应用程序进程启动过程,需要注意的是“应用程序进程启动过程”,而不是应 ...

  5. WiFi 统一管理以及设备自动化测试实践

    ATX 安卓设备 WiFi 统一管理以及设备自动化测试实践 (零散知识梳理总结) 此文为转载,感谢作者  目录  众所周知,安卓单台设备的UI自动化测试已经比较完善了,有数不清的自动化框架或者工具.但 ...

  6. 安卓socket聊天

    安卓基于Socket通信(服务器配合) 1.话不多说进入正题,先创建服务端,在Android Studio中创建Java代码,如下图所示: 选择Java Library 需要改名字的自己随意 2.创建 ...

  7. react native中的聊天气泡以及timer封装成的发送验证码倒计时

    今天看来情书写的文章,研究了一下大佬写的文章,自己做一点总结. 其实,今天我想把我近期遇到的坑都总结一下:1.goBack的跨页面跳转,又两种方法,一可以像兔哥那样修改navigation源码,二可以 ...

  8. 《SQLSERVER2012实施与管理实战指南》前4章节笔记内容

    <SQLSERVER2012实施与管理实战指南>前4章节笔记内容 <SQLSERVER2012实施与管理实战指南>的前面4章是<SQLSERVER企业级平台管理实践> ...

  9. Inception开源公告

    关于Inception MySQL语句的审核,在业界都已经基本被认同了,实际上也是对MySQL语句写法的统一化,标准化,而之前的人工审核,针对标准这个问题其实是很吃力的,标准越多,DBA越累,开发也越 ...

  10. 将 Windows VM 移到其他 Azure 订阅或资源组

    本文逐步说明如何在资源组或订阅之间移动 Windows VM. 如果最初在个人订阅中创建了 VM,现在想要将其移到公司的订阅以继续工作,则在订阅之间移动 VM 可能很方便. Important 不可在 ...