我遇到了一个问题,我为不同的词典获得了相同的哈希值。也许我做了一些明显错误的事情,但我认为,具有不同内容的对象(a.k.a。不等于对象)应该具有不同的哈希值。
NSDictionary *dictA = @{ @"foo" : @YES };
NSDictionary *dictB = @{ @"foo" : @NO };
BOOL equal = [dictA hash] == [dictB hash];
NSAssert(!equal, @"Assuming, that different dictionaries have different hash values.");
有什么想法吗?
答案 0 :(得分:23)
无法保证两个不同的对象具有不同的哈希值。
在最新的open-source version of CoreFoundation中,CFDictionary的哈希值(相当于NSDictionary)定义为:
static CFHashCode __CFDictionaryHash(CFTypeRef cf) {
return __CFBasicHashHash((CFBasicHashRef)cf);
}
and __CFBasicHashHash
is defined as:
__private_extern__ CFHashCode __CFBasicHashHash(CFTypeRef cf) {
CFBasicHashRef ht = (CFBasicHashRef)cf;
return CFBasicHashGetCount(ht);
}
这只是集合中存储的条目数。换句话说,[dictA hash]
和[dictB hash]
的哈希值都是 1 ,即词典中的条目数。
虽然这是一个非常糟糕的哈希算法,但CF在这里没有做错任何事。如果您需要更准确的哈希值,可以在Obj-C类别中自行提供。
答案 1 :(得分:3)
使用只有整数,字符串等的字典。我会使用dict.description.hash
作为快速代码。
答案 2 :(得分:1)
基于igor-kulagin答案的解决方案,不依赖于顺序:
@implementation NSDictionary (Extensions)
- (NSUInteger) hash
{
NSUInteger prime = 31;
NSUInteger result = 1;
for (NSObject *key in [[self allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
result = prime * result + [key hash];
result = prime * result + [self[key] hash];
}
return result;
}
@end
但是,如果字典包含其他字典作为值,则仍有可能发生冲突。
答案 3 :(得分:1)
功能'哈希'不是真正的哈希函数。它为字符串提供了不同的值(所有可预测的),但对于集合(数组和字典),它只返回计数。如果你想要一个唯一的哈希,你可以自己使用素数或函数srandom()和random()来计算它,或者探索像CommonCrypto / CommonDigest.h中可用的SHA1这样的实际哈希函数
答案 4 :(得分:-2)
我根据以下答案创建了 NSDictionary 类别并覆盖了哈希方法:Best practices for overriding isEqual: and hash
@implementation NSDictionary (Extensions)
- (NSUInteger) hash {
NSUInteger prime = 31;
NSUInteger result = 1;
NSArray *sortedKeys = [self.allKeys sortedArrayUsingSelector: @selector(compare:)];
for (NSObject *key in sortedKeys) {
result = prime * result + key.hash;
id value = self[key];
if ([value conformsToProtocol: @protocol(NSObject)] == YES) {
result = prime * result + [value hash];
}
}
return result;
}
@end
Swift实施。
extension Dictionary where Key: Comparable, Value: Hashable {
public var hashValue: Int {
let prime = 31
var result = 1
let sortedKeys = self.keys.sorted()
for (key) in sortedKeys {
let value = self[key]!
result = Int.addWithOverflow(Int.multiplyWithOverflow(prime, result).0, key.hashValue).0
result = Int.addWithOverflow(Int.multiplyWithOverflow(prime, result).0, value.hashValue).0
}
return result
}
}
完美地说,这还需要为Equatable
实施Dictionary
协议,这样您也可以添加Hashable
协议一致性。