# 数组、集合、字典与 hash、isEqual 方法的关联

1. NSArray 允许重复添加元素，添加元素的时候不查重，所以不会调用上面2个方法。在移出元素的时候会依次遍历数组内的元素，每个元素调用 **isEqual** 方法（remove 方法传入的元素作为参数），所有返回真值的元素都会被移除。在字典中不涉及 hash 方法。

2. NSSet 不允许重复添加元素，所以在添加新元素的时候，该元素的 **hash** 方法会被调用，若集合中不存在与此元素 hash 相同的元素，则它会被直接加入集合，不调用 **isEqual** 方法；若存在，则依次调用该集合每个元素的 **isEqual** 方法，返回真值则判等，不加入，处理结合，若返回 false， 则判定集合内不存在该元素，将其加入。

3. 集合中移除元素时，首先调用它的 **hash方法**，若集合中存在与其 **hash值** 相等的元素，则调用该元素的 **isEqual方法**，若返回真值则判断，进行移除；若不存在，则会依次调用集合中每个元素的 **isEqual方法**，只要找到一个返回真值的元素，就进行移除，并结束整个过程（所有这样会有其它满足 isEqual 方法但却漏掉未被删除的元素）。调用 contains 方法时类似

4. 因此如果自定义对象被加入到集合或作为字典的 key 时，需要同时重写 isEqual 方法和 hash 方法，这样，若集合存在某元素，则调用它的 contains 和 remove 方法时，可以在 O(1) 完成查询，否则需要 O(n) 完成。
　
5. 需要注意的是，NSDictionary 的 key、value 都说对象类型即可，但是被设为 key 的对象需要遵循 NSCopying 协议。

6. hash 方法出现的目的是：当我们从数组中查找元素时，需要依次遍历数组中的元素，时间复杂度为 O(n),为了解决效率问题，引入了 Hash Table 方法。当添加元素的时候，为每个元素设置了 hash 值，这样当下次查找的时候就直接通过 hash 值找到对应的位置，时间复杂度为 O(1)。设计一个合理的 hash 算法的指标是对于每个参数，其返回的 hash 值唯一。

7. 查阅资料可知，一个推荐的自定义对象的 **hash** 算法是将关键属性的 hash 值，按照位或运算
    
```
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSDate *birthday;
@end


- (NSUInteger)hash{
   return [self.name hash] ^ [self.birthday hash];
}
```



8. NSObject 类中的 equal 方法的判断是包括内存地址的，也就是说，NSObject 若想判断2个对象相等，那么这2个对象的内存地址必须相等



