boost::hash
has hashing functions for most builtin types including containers.
But as stated in the boost::hash_range
function description, the hash algorithm for ranges
is sensitive to the order of the elements so it wouldn't be appropriate to use this with an unordered container
And thus there is no boost::hash
specialization for std::unordered_map
nor boost::unordered_map
.
The question is:
Is there an "easy and efficient" way to hash an unordered_map
without reimplementing a hash algorithm from scratch ?
The unordered_map::hash_function() is a built in function in C++ STL which is used to get the hash function. This hash function is a unary function which takes a single argument only and returns a unique value of type size_t based on it.
Yes, HashMap in java and unordered_map in c++ stl are more or less the same… you can store pointers in java too(Don't forget, in java, everything you deal with is a reference)… hence, HashMap also serves your purpose.
Unordered_map IS a hash table actually. You may want to use a better example as, as is the second insert will fail since it has the same key.
Iterating over a map by using STL Iterator: By creating an iterator of std::map and initializing it to the starting of map and visiting upto the end of map we can successfully iterate over all the elements of map.
The problem here is that there is no guarantee that the items even have an ordering among them.
So, sorting the items may very well not work for arbitrary unordered containers. You have 2 options:
You can of course convert the unordered_map
to some other data structure that has a guaranteed order and use that to generate the hash.
A better idea might be to hash each individual element of the map, put those hashes into a vector
, then sort and combine the hashes. See for example How do I combine hash values in C++0x? to combine the hashes.
template<typename Hash, typename Iterator>
size_t order_independent_hash(Iterator begin, Iterator end, Hash hasher)
{
std::vector<size_t> hashes;
for (Iterator it = begin; it != end; ++it)
hashes.push_back(hasher(*it));
std::sort(hashes.begin(), hashes.end());
size_t result = 0;
for (auto it2 = hashes.begin(); it2 != hashes.end(); ++it2)
result ^= *it2 + 0x9e3779b9 + (result<<6) + (result>>2);
return result;
}
Testing this on shuffled vectors shows that it always returns the same hash.
Now to adapt that basic concept to work specifically with unordered_map
. Since the iterator of unordered_map
returns a pair
, we need a hash function for that too.
namespace std
{
template<typename T1, typename T2>
struct hash<std::pair<T1,T2> >
{
typedef std::pair<T1,T2> argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const& s) const
{
result_type const h1 ( std::hash<T1>()(s.first) );
result_type const h2 ( std::hash<T2>()(s.second) );
return h1 ^ (h2 + 0x9e3779b9 + (h1<<6) + (h1>>2));
}
};
template<typename Key, typename T>
struct hash<std::unordered_map<Key,T> >
{
typedef std::unordered_map<Key,T> argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const& s) const
{
return order_independent_hash(s.begin(), s.end(), std::hash<std::pair<Key,T> >());
}
};
}
See it in action: http://ideone.com/WOLFbc
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With