Objective-C -- isEqual與hash

為什么要有isEqual方法


對于對象類型, ==運算符比較的是對象的地址,即是否為同一對象西傀。

對象地址相等不代表對象相等斤寇,即對象地址相等是對象相等的必要非充分條件。

isEqual方法就是用來判斷兩個對象是否相等池凄。

isEqual的默認實現


isEqual方法是NSObject中聲明的抡驼,默認實現就是簡單的比較對象地址。

@implementation NSObject (Approximate)
- (BOOL)isEqual:(id)object {
  return self == object;
}
@end

NSObject的子類可以實現自己的isEqual:方法肿仑,一般方式如下:

  • 實現新的isEqualTo__ClassName__: 方法致盟,用來做具體屬性值的對比碎税,我們叫做高層比較方法
  • 重載isEqual:,先做對象地址的比較,以及對象類型的比較馏锡,然后調用高層比較方法
  • 重載hash:,這個方法后面再講

如果是集合類型的話雷蹂,還應該做深度判等,即所有成員一一進行比較杯道。

綜合舉例如下:

@implementation NSArray (Approximate)
- (BOOL)isEqualToArray:(NSArray *)array {
  if (!array || [self count] != [array count]) {
    return NO;
  }

  for (NSUInteger idx = 0; idx < [array count]; idx++) {
      if (![self[idx] isEqual:array[idx]]) {
          return NO;
      }
  }

  return YES;
}

- (BOOL)isEqual:(id)object {
  if (self == object) {
    return YES;
  }

  if (![object isKindOfClass:[NSArray class]]) {
    return NO;
  }

  return [self isEqualToArray:(NSArray *)object];
}
@end

在Foundation中匪煌,很多NSObject的子類已經定義好了自己的isEqual,及其高層比較方法:

  • NSAttributedString -isEqualToAttributedString:
  • NSData -isEqualToData:
  • NSDate -isEqualToDate:
  • NSDictionary -isEqualToDictionary:
  • NSHashTable -isEqualToHashTable:
  • NSIndexSet -isEqualToIndexSet:
  • NSNumber -isEqualToNumber:
  • NSOrderedSet -isEqualToOrderedSet:
  • NSSet -isEqualToSet:
  • NSString -isEqualToString:
  • NSTimeZone -isEqualToTimeZone:
  • NSValue -isEqualToValue:

NSArray中isEqual相關實踐


看一段示例代碼

    NSString* name1 = @"小明";
    NSMutableArray* array = [NSMutableArray arrayWithObjects:name1,nil];
    NSString* name2 = [NSString stringWithFormat:@"小明"];
    NSLog(@"name1:%p,name2:%p",name1,name2);
    NSLog(@"[array contain:name2]=%d",[array containsObject:name2]);
    [array removeObject:name2];
    NSLog(@"[array remove:name2].count=%zd",array.count);

打印結果

name1:0x108e58200,name2:0x600000436780
[array contain:name2]=1
[array remove:name2].count=0

可見党巾,NSArray的containsObject:removeObject:方法都是使用了isEqual來判斷成員是否相等的萎庭。removeObject:會把所有相等的成員都移除掉。

為什么要有hash方法


我們在數組中查找某個成員齿拂,在數組未排序的情況下, 查找的時間復雜度是O(array_length)

為了提高查找的速度, Hash Table出現了驳规。當成員被加入到Hash Table中時, 會給它分配一個hash值, 以標識該成員在集合中的位置,通過這個位置標識可以將查找的時間復雜度優(yōu)化到O(1), 當然如果多個成員都是同一個位置標識, 那么查找就不能達到O(1)了

重點來了:

分配的這個hash值(即用于查找集合中成員的位置標識), 就是通過hash方法計算得來的, 且hash方法返回的hash值最好唯一

和數組相比, 基于hash值索引的Hash Table查找某個成員的過程就是

Step 1: 通過hash值直接找到查找目標的位置

Step 2: 如果目標位置上有多個相同hash值得成員, 此時再按照數組方式進行查找

hash方法什么時候被調用?


HashTable是一種基本的數據結構署海,NSSet和NSDictionary都是使用了HashTable存儲數據吗购,因此可以確保它們查詢成員的速度為O(1)。而NSArray使用了順序表存儲數據砸狞,查詢速度為O(n)捻勉。

hash方法只在對象被添加至NSSet和設置為NSDictionary的key時會調用
NSSet添加新成員時, 需要根據hash值來快速查找成員, 以保證集合中是否已經存在該成員
NSDictionary在查找key時, 也利用了key的hash值來提高查找的效率

hash和isEqual的關系


  • 對象的判等是相互的 ([a isEqual:b] ? [b isEqual:a])
  • 如果兩個對象相等,那么它們的hash值一定相等 ([a isEqual:b] ? [a hash] == [b hash])
  • 值相等刀森,兩個對象不一定相等 ([a hash] == [b hash] ?? [a isEqual:b])

總之踱启,hash值是對象判等的必要非充分條件

為了優(yōu)化判等的效率, 基于hash的NSSet和NSDictionary在判斷成員是否相等時, 會這樣做

Step 1: 集成成員的hash值是否和目標hash值相等, 如果相同進入Step 2, 如果不等, 直接判斷不相等

Step 2: hash值相同(即Step 1)的情況下, 再進行對象判等, 作為判等的結果

如何重寫自己的hash方法


hash方法是NSObject中聲明的,默認實現是返回對象的內存地址研底。

那么hash方法的最佳實踐到底是什么呢?
大神Mattt ThompsonEquality中給出的結論就是

In reality, a simple XOR over the hash values of critical properties is sufficient 99% of the time(對關鍵屬性的hash值進行位異或運算作為hash值)

比如對于Person類的hash方法實現如下

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

參考文章

iOS開發(fā) 之 不要告訴我你真的懂isEqual與hash!
Equality

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末禽捆,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子飘哨,更是在濱河造成了極大的恐慌,老刑警劉巖琐凭,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芽隆,死亡現場離奇詭異,居然都是意外死亡统屈,警方通過查閱死者的電腦和手機胚吁,發(fā)現死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愁憔,“玉大人腕扶,你說我怎么就攤上這事《终疲” “怎么了半抱?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵脓恕,是天一觀的道長。 經常有香客問我窿侈,道長炼幔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任史简,我火速辦了婚禮乃秀,結果婚禮上,老公的妹妹穿的比我還像新娘圆兵。我一直安慰自己跺讯,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布殉农。 她就那樣靜靜地躺著刀脏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪统抬。 梳的紋絲不亂的頭發(fā)上火本,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音聪建,去河邊找鬼钙畔。 笑死,一個胖子當著我的面吹牛金麸,可吹牛的內容都是我干的擎析。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼挥下,長吁一口氣:“原來是場噩夢啊……” “哼揍魂!你這毒婦竟也來了?” 一聲冷哼從身側響起棚瘟,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤现斋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后偎蘸,有當地人在樹林里發(fā)現了一具尸體庄蹋,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年迷雪,在試婚紗的時候發(fā)現自己被綠了限书。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡章咧,死狀恐怖倦西,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情赁严,我是刑警寧澤扰柠,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布粉铐,位于F島的核電站,受9級特大地震影響耻矮,放射性物質發(fā)生泄漏秦躯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一裆装、第九天 我趴在偏房一處隱蔽的房頂上張望踱承。 院中可真熱鬧,春花似錦哨免、人聲如沸茎活。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽载荔。三九已至,卻和暖如春采桃,著一層夾襖步出監(jiān)牢的瞬間懒熙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工普办, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留工扎,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓衔蹲,卻偏偏與公主長得像肢娘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子舆驶,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容