zoukankan      html  css  js  c++  java
  • NSDictionary实现原理-ios哈希hash和isEqual

    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.   
    3. @interface KeyValuePairs: NSObject <NSCopying>  
    4. @property (nonatomic,strong)NSString *identifier;  
    5. @property (nonatomic,strong)NSString *name;  
    6.   
    7. @end  
    KeyValuePairs.m:
    [objc] view plain copy
     
    1. #import "KeyValuePairs.h"  
    2.   
    3. @implementation KeyValuePairs  
    4.   
    5. - (id)copyWithZone:(NSZone *)zone  
    6. {  
    7.     KeyValuePairs *kvp = [[[self class] allocWithZone:zone] init];  
    8.     kvp.identifier = self.identifier;  
    9.     kvp.name = self.name;  
    10.     return kvp;  
    11. }  
    12.   
    13. - (BOOL)isEqualToKeyValuePairs:(KeyValuePairs *)kvp{  
    14.     if (!kvp) {  
    15.         return NO;  
    16.     }  
    17.     BOOL haveEqualName = (!self.name && !kvp.name) || [self.name isEqualToString:kvp.name];  
    18.     BOOL haveEqualIdentifier = (!self.identifier && !kvp.identifier) || [self.identifier isEqualToString:kvp.identifier];  
    19.       
    20.     return haveEqualName && haveEqualIdentifier;  
    21. }  
    22.   
    23. #pragma mark -NSObject  
    24. -(BOOL)isEqual:(id)object{  
    25.     if (self == object) {  
    26.         return YES;  
    27.     }  
    28.     if (![object isKindOfClass:[KeyValuePairs class]]) {  
    29.         return NO;  
    30.     }  
    31.     return [self isEqualToKeyValuePairs:(KeyValuePairs *)object];  
    32. }  
    33.   
    34. - (NSUInteger)hash {  
    35.     return [self.name hash] ^ [self.identifier hash];  
    36. }  
    37.   
    38. @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.   
    14. for (int j = 0; j<1000; j++) {  
    15.     int index = arc4random()%1000;  
    16.     KeyValuePairs *kvp = [names objectAtIndex:index];  
    17.     NSString *strObj = [namesWillUpdateDic objectForKey:kvp];  
    18.     NSString *msg = [NSString stringWithFormat:@"index:%d,identifier:%@,email:%@,strObj:%@",index,kvp.identifier,kvp.name,strObj];  
    19.     NSLog(@"%@",msg);  
    20. }  
     
     
     
    注意:
    1、自定义类为什么一定要实现NSCopying协议呢?这是因为通过key-value把2个对象加入到字典中,字典会对key进行copy一份的操作,而对value对象进行retain操作,如果自定义的类不实现copy协议,那么就不能作为字典的key对象使用。
    2、如果自定义类不重写isEqual则默认使用内存地址比较两个对象,可能会出现意想不到的结果
    3、isEqual和hash方法要同时重写,否则isEqual方法判断将不正确
     
     
     
  • 相关阅读:
    2.变量、数据类型、数据类型转换相关函数
    3.字符串、列表、元组、字典、集合的补充
    CentOS7安装mysql后无法启动服务,提示Unit not found
    (个人记录)Python2 与Python3的版本区别
    1.print()与input()
    JAVA:函数的定义、使用
    Java:包装类
    Java:访问权限
    Java程序流程控制:判断结构、选择结构、循环结构
    Java:运算符的问题
  • 原文地址:https://www.cnblogs.com/stevenwuzheng/p/7347915.html
Copyright © 2011-2022 走看看