I've got a simple wrapper-class for std::unordered_map
that updates a running hash-code for the unordered_map
's contents, as key-value pairs are added or removed; that way I never have to iterate over the entire contents to get the current hash code for the set. It does this by adding to the _hash
member-variable whenever a new key-value pair is added, and subtracting from the _hash
member-variable whenever an existing key-value pair is removed. This all works fine (but see the toy implementation below if you want a code-example of what I mean).
My only concern is that I suspect that simply adding and subtracting values from _hash
might not be the optimal thing to do from the perspective of minimizing the likelihood of hash-value collisions. Is there a mathematically better way to compute the running-hash-code for the table, that would still preserve my ability to efficiently add/remove items from the table (i.e. without forcing me to iterate over the table to rebuild a hash code from scratch every time?)
#include <functional>
#include <unordered_map>
#include <string>
#include <iostream>
template<typename KeyType, typename ValueType> class UnorderedMapWithHashCode
{
public:
UnorderedMapWithHashCode() : _hash(0) {/* empty */}
void Clear() {_map.clear(); _hash = 0;}
void Put(const KeyType & k, const ValueType & v)
{
Remove(k); // to deduct any existing value from _hash
_hash += GetHashValueForPair(k, v);
_map[k] = v;
}
void Remove(const KeyType & k)
{
if (_map.count(k) > 0)
{
_hash -= GetHashValueForPair(k, _map[k]);
_map.erase(k);
}
}
const std::unordered_map<KeyType, ValueType> & GetContents() const {return _map;}
std::size_t GetHashCode() const {return _hash;}
private:
std::size_t GetHashValueForPair(const KeyType & k, const ValueType & v) const
{
return std::hash<KeyType>()(k) + std::hash<ValueType>()(v);
}
std::unordered_map<KeyType, ValueType> _map;
std::size_t _hash;
};
int main(int, char **)
{
UnorderedMapWithHashCode<std::string, int> map;
std::cout << "A: Hash is " << map.GetHashCode() << std::endl;
map.Put("peanut butter", 5);
std::cout << "B: Hash is " << map.GetHashCode() << std::endl;
map.Put("jelly", 25);
std::cout << "C: Hash is " << map.GetHashCode() << std::endl;
map.Remove("peanut butter");
std::cout << "D: Hash is " << map.GetHashCode() << std::endl;
map.Remove("jelly");
std::cout << "E: Hash is " << map.GetHashCode() << std::endl;
return 0;
}