I ran into an issue, where I got the same hash value for different dictionaries. Maybe I'm doing something obvious wrong, but I thought, objects with different content (a.k.a. not equal objects) should have different hash values.
NSDictionary *dictA = @{ @"foo" : @YES };
NSDictionary *dictB = @{ @"foo" : @NO };
BOOL equal = [dictA hash] == [dictB hash];
NSAssert(!equal, @"Assuming, that different dictionaries have different hash values.");
Any thoughts?
With a dictionary with only integers, strings etc. I would use
dict.description.hash
as a quick code.The function 'hash' is not a real hash function. It gives different values for strings (all predictable) but for collections (arrays and dictionaries) it just returns the count. If you want a unique hash you can calculate it yourself using primes, or the functions srandom() and random() or explore a real hash function like SHA1 available in CommonCrypto/CommonDigest.h
There is no guarantee that two different objects will have different hash values.
In the latest open-source version of CoreFoundation, the hash of a CFDictionary (which is equivalent to an NSDictionary) is defined as:
and
__CFBasicHashHash
is defined as:which is simply the number of entries stored in the collection. In the other words, both
[dictA hash]
and[dictB hash]
's hash value are 1, the number of entries in the dictionaries.While it is a very bad hash algorithm, CF didn't do anything wrong here. If you need to have a more accurate hash value, you can provide one yourself in an Obj-C category.
A solution based on igor-kulagin's answer which is not order dependent:
However, there is still a possibility of collision if the dictionary contains other dictionaries as values.
I created NSDictionary category and overridden hash method based on this answer: Best practices for overriding isEqual: and hash
And Swift implementation.
Perfectly this also requires to implement
Equatable
protocol forDictionary
so you can also addHashable
protocol conformance.