I don't quite get the purpose of this data structure. What's the difference between std::multimap<K, V>
and std::map<K, std::vector<V>>
. The same goes for std::multiset
- it could just be std::map<K, int>
where the int counts the number of occurrences of K. Am I missing something on the uses of these structures?
A multimap can store more than one value against a key. Both keys and values are stored in the form of a tuple. A multimap is similar to a map in C+; the only difference is that a multimap can hold keys that are not unique (unlike in a standard map).
The map and the multimap are both containers that manage key/value pairs as single components. The essential difference between the two is that in a map the keys must be unique, while a multimap permits duplicate keys.
The C++ Standard Library multimap class is used for the storage and retrieval of data from a collection in which each element is a pair that has both a data value and a sort key. The value of the key doesn't need to be unique and is used to order the data automatically.
Multimap is similar to map with an exception that multiple elements can have same keys. The key value and mapped value pair has to be unique in multimap. mm::find() – Returns an iterator to the element with key value 'b' in the multimap if found, else returns the iterator to end.
A counter-example seems to be in order.
Consider a PhoneEntry in an AdressList grouped by name.
int AdressListCompare(const PhoneEntry& p1, const PhoneEntry& p2){
return p1.name<p2.name;
}
multiset<PhoneEntry, AdressListCompare> adressList;
adressList.insert( PhoneEntry("Cpt.G", "123-456", "Cellular") );
adressList.insert( PhoneEntry("Cpt.G", "234-567", "Work") );
// Getting the entries
addressList.equal_range( PhoneENtry("Cpt.G") ); // All numbers
This would not be feasible with a set+count. Your Object+count approach seems to be faster if this behavior is not required. For instance the multiset::count() member states
"Complexity: logarithmic in size + linear in count."
You could use make the substitutions that you suggest, and extract similar behavior. But the interfaces would be very different than when dealing with regular standard containers. A major design theme of these containers is that they share as much interface as possible, making them as interchangeable as possible so that the appropriate container can be chosen without having to change the code that uses it.
For instance, std::map<K, std::vector<V>>
would have iterators that dereference to std::pair<K, std::vector<V>>
instead of std::pair<K, V>
. std::map<K, std::vector<V>>::Count()
wouldn't return the correct result, failing to account for the duplicates in the vector. Of course you could change your code to do the extra steps needed to correct for this, but now you are interfacing with the container in a much different way. You can't later drop in unordered_map
or some other map implementation to see it performs better.
In a broader sense, you are breaking the container abstraction by handling container implementation details in your code rather than having a container that handles it's own business.
It's entirely possible that your compiler's implementation of std::multimap
is really just a wrapper around std::map<K, std::vector<V>>
. Or it might not be. It could be more efficient and friendly to object pool allocation (which vectors are not).
Using std::map<K, int>
instead of std::multiset
is the same case. Count()
would not return the expected value, iterators will not iterate over the duplicates, iterators will dereference to std::pair<k, int>
instead of directly to `K.
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