Reputation: 2270
I have some keys that are pair<string, string>
. I was originally going to write my own hash function but thought that it might be easier to just implement an unordered_map<string, unordered_map<string, val>>
. Are there any performance differences between these two I should be aware of?
Upvotes: 2
Views: 1448
Reputation: 2438
Defining a simple hash function in your case is trivial and performant. If the std::pair
is semantically the key, then this approach makes your intent clear. It also allows duplicates of the first member of the std::pair
in your map, as you only need the entire key to be unique. In terms of usage, you also avoid the additional layer of indirection, with nested maps.
Example implementation:
...
using pairSS = std::pair<std::string, std::string>;
namespace std
{
template<> struct hash<pairSS>
{
std::size_t operator()(pairSS const& pair) const noexcept
{
return std::hash<std::string>{}(pair.first) ^
(std::hash<std::string>{}(pair.second) << 1);
}
};
}
int main()
{
std::pair myPair = {"Hi", "bye"};
std::cout << std::hash<pairSS>{}(myPair) << std::endl;
struct val{};
std::unordered_map<pairSS, val> hashMap;
}
Upvotes: 1
Reputation: 24412
I would use std::unordered_map<std::pair<std::string, std::string>, Value, [pair_hash][1]>
for two reasons:
Of course, you can measure your two versions with your favorite profiler, but basing on my experience - the number of allocation is what matters here the most - so see:
flat_map.insert(key, value));
will create on average just one new bucket (or extend one), whilst
auto it = map2.insert(make_pair(key.first, map1{}));
it->second.insert(make_pair(key.second, value));
have to create empty map1 - what might not be zero-cost. Then it has to add/extend two buckets (list associated with the given hash value).
The Second reason is more important for me. Flat(one) map is easy to use. You could see in insert example already that it is more complicated, but consider erase - it is so complicated, that it is easy to make a mistake:
void remove(
std::unordered_map<std::string,
std::unordered_map<std::string, Value>>& map,
std::pair<std::string, std::string> const& key)
{
auto it1 = map.find(key.first);
if (it1 == map.end()) return;
it1->second.erase(key.second);
// easy to forget part
if (it1->second.empty())
{
map.erase(it1);
}
}
Upvotes: 1