对象的比较

前言

在面试今日头条的时候,上来那个小哥哥就让我写个对象的比较。我以为是简简单单的在类里加入一个比较的方法,一切显得那么简单。
小哥哥看了后,告诉我好好学学哈希算法。
今天还有一个小时下班,争取在一个小时内总结完毕。
一个小时根本总结不完,第二天我胡汉三由回来了。

大纲

  • NSCopying协议
  • 深拷贝,浅拷贝
  • 用strong和copy修饰字符串
  • 字典底层实现原理
  • 对象的比较

1. NSCopying协议

如果想让一个对象支持copy功能,就需要遵守NSCopying协议,并且实现。
主要是实现(id)copyWithZone:(NSZone *)zone方法,下面是例子

- (id)copyWithZone:(NSZone *)zone{    
     PersonModel *model = [[[self class] allocWithZone:zone] init];
     model.firstName = self.firstName;
     model.lastName  = self.lastName;
     //未公开的成员
     model->_nickName = _nickName;
     return model;
}

需要注意2点:

  • 最好调用[self class],self可能指向的是子类
  • 想让拷贝的对象可变,用NSMutabeCopying;不可变用NSCopying

2. 深拷贝浅拷贝

深拷贝拷贝对象本身的所有信息到了另一块内存之中。
浅拷贝仅仅拷贝了指针,并没有开辟内存,只要内存中的值改变了,所有指向这个内存的指针对应的值都会改变。

还可以多讲讲,mutablecopy和copy。
属性用copy修饰完后,给它赋值的时候,如果对方是不可变类型,直接复制地址就行
例如:NSString * name=@"I am a little kino rabiit!"
此时有个属性@property(nonatomic,copy)NSString * myName;
可以self.myName = [name copy]; self.myName = name;直接复制地址
如果对方是可变类型,并且myName使用copy修饰。
NSMutableString * name=@"I am a little kino rabiit!"
self.myName=name;与self.myName=[name copy];是一样的。都是另外开辟内存拷贝一份对象。这两个对象地址是不一样的。

3. 用strong和copy修饰字符串

如果是不可变对象,用strong与copy修饰都是一样的
如果是可变字符串,建议用copy修饰,预防一改全变的情况发生。

3. 字典底层实现原理

大部分语言的字典都是通过哈希表来实现的。
哈希概念:哈希表的本质是一个数组,数组中每一个元素称为一个箱子(bin),箱子中存放的是键值对。
假设,箱子个数为n。先根据key算出hash值,所放在的箱子位置=hash%n;
如果冲突了,有开放寻址法,拉链法。我个人比较喜欢拉链法。
再来介绍一个概念: 负载因子(load factor)
计算方式是:同个箱子的键值对的个数 / n;
如果负载因子数越大,哈希表越满,越容易导致冲突,性能也就越低。因此,一般来说,当负载因子大于某个常数(可能是 1,或者 0.75 等)时,哈希表将自动扩容。

重哈希概念:

哈希表在自动扩容时,一般会创建两倍于原来个数的箱子,因此即使 key 的哈希值不变,对箱子个数取余的结果也会发生改变,因此所有键值对的存放位置都有可能发生改变,这个过程也称为重哈希(rehash)。



哈希表的扩容并不总是能够有效解决负载因子过大的问题。假设所有 key 的哈希值都一样,那么即使扩容以后他们的位置也不会变化。虽然负载因子会降低,但实际存储在每个箱子中的链表长度并不发生改变,因此也就不能提高哈希表的查询性能。

4. 对象的比较

我的研究步骤:

  1. 先说一下==
    对于基本对象 == 比较的是值
    对于oc对象, == 比较的是地址

系统提供的类,NSString、NSArray、NSDictionary等系统已经实现了-isEquel和-hash方法;
我亲自对字符串做了测试。
只要是字符串的内容一样,调用==和isEqual方法都是相等的。
我同时对数组做了测试,用==判断不等,用isEqual是相等的

如果我们想让对象的属性相等的话,那么就判断这俩对象相等。这时候我们需要自己去重写isEqual方法和hash方法。

为什么重写isEqual方法必许重写hash方法呢?
因为某某约定说,相同的对象必须有相同的哈希值。

重写isEqual方法三部曲
第一:判断传进来的参数是否跟自己地址一样
第二:判断类名是否跟自己类名一样
第三:按照要求判断属性是否一样
代码如下:

- (BOOL)isEqualToMyCalss:(MyCalss *)object {
    //判断传进来的参数是否跟自己地址一样
    if (self == object) {
        return YES;
    }
    //判断类名是否跟自己类名一样
 if ([self class] != [object class]) {
 return NO;
}
    //判断  如果对象的属性值
    省略。。。
}
- (NSUInteger)hash {
  NSUInteger prop1Hash = [_prop1 hash];
  NSUInteger prop2Hash = [_prop2 hash];
  return  prop1Hash ^ prop2Hash;
}
  1. 我创建了一个Person类,果然有isEqual方法。


    图片1-1

    那我把它跟自己做个比较,看看输出什么。把自己跟另一个新创建出来的对象相比是否一样。


    图片1-2

    总结一下:每个新创建出来的对象都是不一样的。无论是调用==还是isEqual方法,都是不一样的。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容