欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

IsEqual与Hash个人理解,

来源: 开发者 投稿于  被查看 6139 次 评论:36

IsEqual与Hash个人理解,


IsEqual与Hash个人理解

isEqual

NSObject类的实例方法: - (BOOL)isEqual:(id)object 主要是根据对象的内存地址来判断两个对象是否相等,这里与 ==效果相同。

  • isEqualToString

    (BOOL)isEqualToString:(NSString *)aString 是NSString类的实例方法,它主要用于比较两个字符串中的内容是否相同,而非比较两个字符串所在内存地址。该方法常用。

  • 自定义类的isEqual

    在开发需求中,如果有比较两个类对象是否具有等同性时,通常会根据需求来对父类的isEqual进行改写,这里的做法通常是:

Hash

Hash主要是用于NSMutableSet中的判断添加的新对象是否已经存在了容器当中,如果不存在,则将新对象放入Set容器中,而判断的依据就是根据实例对象的Hash属性。

但是因为不同对象的Hash值会有冲突的可能性(相同的对象Hash值一定相同,这里的相同指的是内存地址相同),所以最后如果哈希值发生冲突的话,则会调用类中的isEqual方法进行等同性判断。

既然都会调用isEqual方法,那么为什么不直接按顺序遍历set容器,依次调用isEqual方法?

答:首先set容器是非顺序容器,它的内存空间结构不是按顺序存储的,如果按插入的顺序遍历,开支很大(当然set的插入是无顺序的)。其次除非是大量数据的存储才会发生冲突的可能,在少量数据的情况下基本上不会出现依次遍历冲突对象的isEqual方法的冲突情况。这样看来以O(1)的时间复杂度就可以完成的任务,当然就选择是它了。

在NSObject对象中,Hash值是根据对象所在的内存空间来进行计算的。所以在修改了isEqual方法后,如果没有修改Hash的setter方法,系统在执行set容器操作该对象时,仍然会以内存地址为准来判断两个对象是否相同。所以为了防止出现这种情况,在修改等同性判断方法的时候应顺便修改Hash的setter方法

  • 自定义Hash方法

    当自定义完isEqual后,一般为了防止后面的开发用到有关set容器出现意外,所以一般都会对Hash的Setter方法进行修改。代码如下:

    -(NSUInteger)hash{
        //这里的字符串只要能体现出类对象的name和age即可(依需求而定)
        NSString* stringToHash=[NSString stringWithFormat:@"%@ %i",_name,_age];
        return [stringToHash hash];
    }
    

    但这里有个弊端,因为在这里新建一个字符串的开销很大(与返回一个属性值相比较)。所以一般采取下面这种策略:

    -(NSUInteger)hash{
        NSUInteger nameHash=[_name hash];
        NSUInteger ageHash=_age;
        return nameHash^ageHash;
    }
    

    这种方法既考虑了开销也考虑了属性的相关性和随机性。---------Effective OC

  • Hash的调用顺序

    例子如下:

    //Student.m
    -(BOOL)isEqualToStudent:(Student *)otherStudent{
        if(self == otherStudent){
            NSLog(@"(%@:%i)与(%@:%i)冲突了",_name,_age,otherStudent.name,otherStudent.age);
            return YES;
        }
        if(![_name isEqualToString:otherStudent.name])
            return NO;
        if(_age != otherStudent.age)
            return NO;
        NSLog(@"[%@:%i]与[%@:%i]冲突了",_name,_age,otherStudent.name,otherStudent.age);
        return YES;
    }
    
    -(BOOL)isEqual:(id)object{
        if([self class] == [object class])
            return [self isEqualToStudent:(Student *)object];
        else
            return [super isEqual:object];
    }
    
    -(NSUInteger)hash{
        return _age;
    }
    
    -(NSString *)description{
        return [NSString stringWithFormat:@"%@:%i",_name,_age ];
    }
    
    //main.m
    Student* stu1 = [[Student alloc] initWithName:@"小明" age:10];
    Student* stu2 = [[Student alloc] initWithName:@"小明" age:10];
    Student* stu3 = [[Student alloc] initWithName:@"小明" age:0];
    
    NSMutableSet* set = [NSMutableSet set];
        
    [set addObject:stu1]; //步骤1
    [set addObject:stu2]; //步骤2
    [set addObject:stu3]; //步骤3
    NSLog(@"%@",set);
    

    输出结果:

    2020-05-08 22:50:12.172207+0800 effective-OC-test[83370:76838657] [小明:10]与[小明:10]冲突了
    2020-05-08 22:50:12.172542+0800 effective-OC-test[83370:76838657] {(
        小明:0,
        小明:10
    )}
    Program ended with exit code: 0
    

总结

相关文章

    暂无相关文章

用户评论