NSDictionary實現原理-ios哈希hash和isEqual
OC中自定義類的NSCopying實現的注意事項(isEqual & hash實現)
iOS開發 之 不要告訴我你真的懂isEqual與hash!
NSDictionary(字典)是使用 hash表來實現key和value之間的映射和存儲的, hash函數設計的好壞影響着數據的查找訪問效率。數據在hash表中分佈的越均勻,其訪問效率越高。而在Objective-C中,一般都是利用NSString 來做爲鍵值,其內部使用的hash函數也是經過使用 NSString對象做爲鍵值來保證數據的各個節點在hash表中均勻分佈。ios
見NSDictionary中最經常使用的一個方法原型:objective-c
- - (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;
從這個方法中能夠知道, 要做爲 Key 值,必須遵循 NSCopying 協議。這是由於在NSDictionary內部,會對 aKey 對象 copy 一份新的。而 anObject 對象在其內部是做爲強引用(retain或strong)。
既然知道了做爲 key 值,必須遵循 NSCopying 協議,說明除了 NSString 對象以外,咱們還可使用其餘類型對象來做爲 NSDictionary 的 key值。不過這還不夠,做爲 key 值,該類型還必須繼承於 NSObject 而且要重載一下兩個方法:dom
- - (BOOL)isEqual:(id)object;
其中,hash 方法是用來計算該對象的 hash 值,最終的 hash 值決定了該對象在 hash 表中存儲的位置。咱們重寫hash方法是由於每向NSDictionary和NSSet中存入一個key-value,字典會先利用即將插入的key的hash和字典中已經存在的全部的key.hash進行比較,最終來決定是新增一個key,仍是覆蓋原有的key。 可是僅僅使用key.hash比較,有時會出現2個對象hash相同的狀況,這時候就須要調用isEqual 方法來最終裁定,2個key對象是否相同。
在OC中,若是自定義類,則要考慮賦值、持久化保存、保存到其它容器中等各類狀況的對象複製和比較,下面是一個比較全面的自定義例子,在此僅做記錄:ide
自定義類:函數
KeyValuePairs.h:測試
- #import <Foundation/Foundation.h>
-
- @interface KeyValuePairs: NSObject <NSCopying>
- @property (nonatomic,strong)NSString *identifier;
- @property (nonatomic,strong)NSString *name;
-
- @end
KeyValuePairs.m:
- #import "KeyValuePairs.h"
-
- @implementation KeyValuePairs
-
- - (id)copyWithZone:(NSZone *)zone
- {
- KeyValuePairs *kvp = [[[self class] allocWithZone:zone] init];
- kvp.identifier = self.identifier;
- kvp.name = self.name;
- return kvp;
- }
-
- - (BOOL)isEqualToKeyValuePairs:(KeyValuePairs *)kvp{
- if (!kvp) {
- return NO;
- }
- BOOL haveEqualName = (!self.name && !kvp.name) || [self.name isEqualToString:kvp.name];
- BOOL haveEqualIdentifier = (!self.identifier && !kvp.identifier) || [self.identifier isEqualToString:kvp.identifier];
-
- return haveEqualName && haveEqualIdentifier;
- }
-
- #pragma mark -NSObject
- -(BOOL)isEqual:(id)object{
- if (self == object) {
- return YES;
- }
- if (![object isKindOfClass:[KeyValuePairs class]]) {
- return NO;
- }
- return [self isEqualToKeyValuePairs:(KeyValuePairs *)object];
- }
-
- - (NSUInteger)hash {
- return [self.name hash] ^ [self.identifier hash];
- }
-
- @end
測試:
- NSMutableDictionary *namesWillUpdateDic = [[NSMutableDictionary alloc] init];
- NSMutableArray *names = [[NSMutableArray alloc] init];
- for (int i = 0; i<1000; i++) {
- NSString *name = [NSString stringWithFormat:@"%d_zhangsan",i];
- NSString *identifier = [NSString stringWithFormat:@"%d_identifier",i];
- NSString *strObj = [NSString stringWithFormat:@"%d_strObj",i];
- KeyValuePairs *kvp = [[KeyValuePairs alloc] init];
- kvp.identifier = identifier;
- kvp.name = name;
- [namesWillUpdateDic setObject:strObj forKey:kvp];
- [names addObject:kvp];
- }
-
- for (int j = 0; j<1000; j++) {
- int index = arc4random()%1000;
- KeyValuePairs *kvp = [names objectAtIndex:index];
- NSString *strObj = [namesWillUpdateDic objectForKey:kvp];
- NSString *msg = [NSString stringWithFormat:@"index:%d,identifier:%@,email:%@,strObj:%@",index,kvp.identifier,kvp.name,strObj];
- NSLog(@"%@",msg);
- }
一、自定義類爲何必定要實現NSCopying協議呢?這是由於經過key-value把2個對象加入到字典中,字典會對key進行copy一份的操做,而對value對象進行retain操做,若是自定義的類不實現copy協議,那麼就不能做爲字典的key對象使用。
二、若是自定義類不重寫isEqual則默認使用內存地址比較兩個對象,可能會出現意想不到的結果
三、isEqual和hash方法要同時重寫,不然isEqual方法判斷將不正確