In practice, when `std::unordered_map` must be used instead of `std::map`?
I know the difference between them, say internal implementation,time complexity for searching element
In that case, you should know that that the average asymptotic element lookup time complexity of unordered map is constant, while the complexity of ordered map is logarithmic. This means that there is some size of container at which point the lookup will be faster when using unordered map.
But I really can't find a circumstance where std::unordered_map could not be replaced by std::map indeed.
If the container is large enough, and if you cannot afford the cost of ordered map lookup, then you cannot choose to replace the faster unordered map lookup.
Another case where ordered map cannot be used is where there doesn't exist a cheap function to compare relative order of the key.
How to find by a key of type std::wstring_view in std::unordered_map<std::wstring, T>?
What you are trying to do is called "heterogeneous lookup" (basically, the type of the map and the type you're trying to use to lookup are different types). In C++20, thanks to P0919, we are going to get new overloads of unordered_map::find()
that will allow what you're trying to do to work.
Until then, the only relevant overload takes, specifically, a Key const&
. And basic_string
's constructor from basic_string_view
is explicit
(see #10). So in C++17, you have to write:
map.find("asdf"s)
or
map.find(std::string("asdf"sv));
unordered_map: Lookup pair of std::string with a pair of std::string_view
With C++20's heterogeneous lookups this can be done (see documentation of unordered_map::find()
). For this to work a hash functor and a equality functor have to be defined, e.g.:
struct hash {
template <typename T>
auto operator()(const std::pair<T, T>& pair) const {
return std::hash<T>{}(pair.first) ^ std::hash<T>{}(pair.second); // not to be used in production (combining hashes using XOR is bad practice)
}
using is_transparent = void; // required to make find() work with different type than key_type
};
struct equal {
template <typename A, typename B>
auto operator()(const std::pair<A, A>& a,
const std::pair<B, B>& b) const {
return a.first == b.first && a.second == b.second;
}
using is_transparent = void; // required to make find() work with different type than key_type
};
The type of the map then has to be changed to std::unordered_map<std::pair<std::string, std::string>, int, hash, equal>
in order to use the defined functors.
find()
now works as intended:
using namespace std::literals;
std::unordered_map<std::pair<std::string, std::string>, int, hash, equal> map{};
map.insert({std::pair{"a"s, "b"s}, 42});
if (auto it = map.find(std::pair{"a"sv, "b"sv}); it != map.end())
std::cout << it->second << std::endl;
if (auto it = map.find(std::pair{"x"s, "y"s}); it != map.end())
std::cout << it->second << std::endl;
// prints 42
The implementation can be played with here
How does std::unordered_map determine the location of a specific key in a hash table?
Typically a hash map keeps an array of buckets inside. A bucket, on the other hand is a list of entries. And so something like this:
template<class TKey, class TValue>
class HashMap {
vector<vector<pair<TKey, TValue>>> Buckets;
};
Then when you do a lookup, it simply takes the key, computes its hash, say hash
, goes to Buckets[hash % Buckets.size]
, which is of type vector<pair<TKey,TValue>>
and does a linear search on it. This makes the amortized (average) lookup complexity constant, while in the worst case (e.g. bad hashing function) it is linear. And indeed, this is the guarantee you get with the unordered_map.
Note that the top level vector will eventually grow when you add elements (maybe even when you remove), in order to allow more entries and avoid collisions. Rehashing is likely to take place in such case. And so adding/removing elements is not trivial, and can be expensive from time to time.
Also note that since vector
allocates memory on the heap under the hood, then there is no issue with using more than one map. They share nothing (well, they may share something, but it doesn't affect the behaviour). But even if an implementation does not use vector
(which is not mandatory, its just an example), some form of dynamic malloc
has to be used to guarantee that.
Using A Struct As Key For std::unordered_map
To use a structure as a key in an unordered_map, you need two things:
A "hasher", something that will take a
const test &
and compute a hash, which defaults tostd:hash<test>
, andA comparison predicate, which defaults to
std::equal_to<test>
.
You've got the second one covered with your spaceship operator, but not the first. There is no std::hash<test>
.
See C++ unordered_map using a custom class type as the key for an example as how to define your own hasher.
How can I get around from using [] operator on const unordered_map in C++?
Use the method "find" which is const :
https://en.cppreference.com/w/cpp/container/unordered_map/find
In your case, something like :
int lookupWithFallback(const StringIntMap& wordcount_map, const std::string& key, int fallbackVal) {
auto it = wordcount_map.find(key);
if(it != wordcount_map.end())
return it->second;
else
return fallbackVal;
}
The operator [] is not const because if the key doesn't exist, it will be created.
https://en.cppreference.com/w/cpp/container/unordered_map/operator_at
Related Topics
Getting a Buffer into a Stringstream in Hex Representation:
Aliasing Struct and Array the C++ Way
Treat C Cstyle Array as Std::Array
What Are Differences Between Std, Tr1 and Boost (As Namespaces And/Or Libraries)
Less Verbose Way to Declare Multidimensional Std::Array
Copy a Std::Vector to a Repeated Field from Protobuf with Memcpy
Getting Std::Thread/Mutex to Work Under Win7 with Mingw and G++ 4.7.2
Why Copy Constructor Is Called When Passing Temporary by Const Reference
How to Iterate Backwards Through an Stl List
Is There a Shorter Way to Write Compound 'If' Conditions
Typeid() Returns Extra Characters in G++
Memset for Initialization in C++
Template Class Member Function Only Specialization
Race-Condition in Pthread_Once()
How to Embed a Http Server in a Google Chrome Extension