isEuqal 与 == 的区别
相信大家都知道在OC中有两种比较是否相等的方法,第一种是直接用==
符号比较,第二种是使用isEqual来比较,它们的区别如下:
==
如果是用于基本数据类型的比较,那么直接比较数值,isEqual只能用于OC对象比较==
如果是用于OC对象比较,那么是判断他们是不是同一个对象,也就是指针所指向的地址是否一致。而isEqual则是比较两个对象是否相同。
接下来用一个颜色的示例来看看:
1 | UIColor *color1 = [UIColor colorWithRed:120/255.0 green:120/255.0 blue:120/255.0 alpha:1]; |
结果输出:
1 | color1.address = 0x60400046af40 |
通过上面的例子我们可以看出,color1和color2是两个不同的对象,所以使用==
来比较的时候他们不相等。但是color1和color2颜色的值都是一样的,所以使用isEuqal来比较的时候他们是相等的。
isEuqal 用于自定义对象的比较
刚刚我们是使用系统的UIColor的对象来比较,如果是我们是自定义的对象,如果需要判断两个对象是否相等的时候,使用isEqual方法来比较是否还起作用呢?
看看以下代码,我们有一个LMPerson
类,里面有两个属性,一个name,一个age,我们创建两个不同的对象,然后给他们赋予同样的name和age,看看使用isEuqal来比较是否相等。
1 | LMPerson *person1 = [LMPerson personWithName:@"lemon" age:18]; |
结果如下:
1 | person1.address = 0x60400022b500 |
通过上面的结果可以看出,当isEqual是用于我们自定义对象的比较的时候,即使我们赋予两个对象属性相同的值,但是返回的却是NO。这是为什么呢?
这是因为UIColor,NSArray,NSdictonary 系统已经帮我们实现了对应的isEqual或者isEqualTo的方法,所以我们如果要用于自定义对象比较,那么也需要实现对应的isEqual方法,接下来我们给LMPerson添加以下实现方法
1 | - (BOOL)isEqual:(id)object{ |
测试结果如下:
1 | person1.address = 0x60000022bac0 |
综上,如果我们要比较两个自定义对象是否相等的时候我们需要重写isEqual方法,给该方法提供一个实现。
什么是hash方法
这个要从hashTable说起,因为hashTabe是无序的集合,并且查找的时间复杂度是O(1),数组是O(array_lenth),为什么hashTable可以做到O(1)呢,因为当一个元素加到hashTable里面的时候,会有一个默认的hash值,用于标记元素在table中的位置,后面如果需要查找该元素,通过hash值可以直接找到该元素。
那么问题来了,这个hash值是怎样得来的呢?
这个hash值其实就是通过- (NSUInteger)hash
方法提供的,并且系统默认的实现就是返回该对象的地址。下面我们来验证这个说法:
我们增加以下方法,并且打印出hash的值。
1 | //LMPerson.m |
测试结果如下:
1 | person1.address = 105553118496736 |
通过结果我们可以知道,其实系统默认的hash方法就是返回对象地址的十进制。
什么时候会调用hash 方法
这里我们直接说结论,如果一个集合中不能出现重复的元素那么就会调用hash方法来判断两个元素是否相等。什么意思呢?
NSMutableArray和NSArray是允许添加重复元素的,所以将一个元素放到该容器中的时候是不会调用hash方法,像NSSet,NSMutableSet元素不能重复,在添加和删除的时候会调用hash方法。当一个元素作为NSDictonary的key的时候,因为key也不能重复,所以也会调用hash方法。大家可以通过将上述创建的两个person对象分别放到不同的集合中进行验证。
值得注意的是,就算hash方法相等也不能判断两个元素就一定是相等,还会调用isEqual来进行判断。也就是说,会优先判断hash是否相等,如果hash不相等那么这两个元素一定不相等,如果hash相等,那么就调用isEqual判断两个元素是否相等,如果返回NO,那么两个元素也不相等, 如果返回YES那么两个元素相等。
也就是说当我们把自定义对象加到NSSet中的或者作为NSDictonary的key的时候 会同时调用hash方法和isEqual方法来判断两个元素是否相等,因此我们需要重写isEqual方法和hash方法。
hash 的正确使用姿势
我们在上面已经验证过如果我们使用系统默认的hash方法来比较两个自定义对象是否相等是不正确的了,那么正确的使用姿势是什么呢?
在Equality这篇文章中,matt大神给了方法,也就是对属性的hash进行异或运算。在LMPerson.m
中
1 | - (NSUInteger)hash{ |
下面我们编写以下代码来测试一下相同的元素是否还能加到hashTable里面
1 | LMPerson *person1 = [LMPerson personWithName:@"lemon" age:18]; |
测试结果:
1 | 2018-08-14 11:08:13.250550+0800 testImageSourceCode[47478:7219161] set.count = 2 |
可以看到我们往hashTable里面添加了三个元素,但是第一和第二个元素是相同的,所以最后加到集合里面的只有两个元素,证明hash方法起作用了。