This question already has answers here:
Use cases of std::multimap
(3 answers)
Closed 6 months ago.
I've just started learning STL containers, and I cannot understand why std::multimap exists. With std::map, we can access values by user-defined keys, but with std::multimap we cannot do that as the latter does not even have an overloaded operator[] and the same key can be mapped to several different values. To me, this looks like std::multimap is essentialy just something like std::multiset<std::pair<K, V>> with the Compare function operating on the K and we lose the main feature of a map which is the ability to access elements by key (as I see it). I've found this post, but still couldn't comprehend the usecases given here. Could someone please give me several examples when we would use std::multimap?
First note that std::map::operator[] is a little quirky. It is not the way to access elements in the map. Instead std::map::operator[] potentially inserts an element into the map and then returns a reference to either the element that was already present before or to the newly inserted. This may seem like splitting hairs, but the difference matters. The way to access a mapped_value given a key in a std::map is std::map::find. std::multimap has a find as well. No big difference with respect to that.
std::map::at has not counterpart in std::multimap because std::map::at returns a reference to the mapped_value for the given key, but in the multimap there can be more than one mapped_value for the same key, so it isnt obvious what a std::multimap::at should return if it existed. Finding and accessing elements can be done with find for both maps.
A std::multimap<K,V> can be compared to a std::map<K,std::vector<V>> but with the interface you'd expect when you want to map more than a single value to the same key. For example std::multimap iterators lets you iterate all key-value pairs in the multimap in one go. With the map of vectors you'd have to use the maps iterators and the vectors iterators. Using the map of vectors with standard algorithms is rather cumbersome.
Further, std::multimap::count returns the number of elements for a given key. With the map of vectors you would have to first find the vector for given key and call its size. This list is not complete, but I hope the difference gets clear.
One example for a multimap could be inhabitants of houses. In the same house lives more than one person. If you want to map street number to person you could use a multimap.
More generally, if you have a collection / container of elements and you want to divide them into distinct groups, you can use a multimap. A common use of std::map (or std::unordered_map) is to count frequencies, eg:
std::map<int,int> freq;
for (const auto& x : some_container) {
++freq[ x % 3 ];
}
This counts how many elements of some_container are divisible by 3, without remainder, with remainder 1, or with remainder 2. If you want to know the elements rather than only count them you can use a std::multimap.
Related
I inserted the elements to the unordered_map with this code:
myMap.insert(std::make_pair("A", 10));
myMap.insert(std::make_pair("B", 11));
myMap.insert(std::make_pair("C", 12));
myMap.insert(std::make_pair("D", 13));
But when I used this command to print the keys
for (const auto i : myMap)
{
cout << i.first << std::endl;
}
they are not in the same order as I inserted them.
Is it possible to keep the order?
No, it is not possible.
Usage of std::unordered_map doesn't give you any guarantee on element order.
If you want to keep elements sorted by map keys (as seems from your example) you should use std::map.
If you need to keep list of ordered pairs you can use std::vector<std::pair<std::string,int>>.
Not with an unordered associative data structure. However, other data structures preserve order, such as std::map which keeps the data sorted by their keys. If you search Stackoverflow a little, you will find many solutions for a data structure with fast key-based lookup and ordered access, e.g. using boost::multi_index.
If it is just about adding values to a container, and taking them out in the order of insertion, then you can go with something that models a queue, e.g. std::dequeue. Just push_back to add a new value, and pop_front to remove the oldest value. If there is no need to remove the values from the container then just go with a std::vector.
Remarkably common request without too many clean,simple solutions. But here they are:
The big library: https://www.boost.org/doc/libs/1_71_0/libs/multi_index/doc/tutorial/index.html Use unique index std::string (or is it char?) and sequenced index to retain the order.
Write it yourself using 2 STL containers: Use a combination of std::unordered_map (or std::map if you want sorted key order traverse as well) and a vector to retain the sequenced order. There are several ways to set this up, depending on the types on your keys/values. Usually keys in the map and the values in the vector. then the map is map<"key_type",int> where the int points to the element in the vector and therefore the value.
I might have a play with a simple template for a wrapper to tie the 2 STL containers together and post it here later...
I have put a proof of concept up for review here:
I went with std::list to store the order in the end, because I wanted efficient delete. But You might choose std::vector if you wanted random access by insertion order.
I used a a list of Pair pointers to avoid duplicate key storage.
I'm having a hard time wrapping my head around unordered_maps and unordered_multimaps because my test code isn't producing what I've been told to expect.
std::unordered_map<string, int> names;
names.insert(std::make_pair("Peter", 4));
names.insert(std::make_pair("George", 4));
names.insert(std::make_pair("George", 4));
When I iterate through this list, I get one instance of George first, then Peter.
1) It's my understanding unordered_maps do not allow multiple keys to map to one value, and that multimaps due. Is this true?
2) Why can Peter and George coexist at a value of 4? What is happening to the second George? And for that matter, why is George appearing first when I iterate from begin() to end() if this is unordered?
3) What is the underlying representation of an unordered map vs. unordered multimap?
4) Is there a way to insert keys into either map without providing a value? E.g. have the compiler create its own hash function that I don't need to worry about when I retrieve keys and look for collisions?
I'll make it short:
No. Multi... refers to keys. A (non-multi)map can't have multiple equivalent keys with differeny values, ie. per key there is at most one value. A multi map can. The same holds for the unordered versions.
Peter != George, which is why they have different key and may very well have the same value.
A hashmap.
Use sets.
In your example the second insertion for George using a (non-multi) is skipped as the same key was previously inserted.
You want to use unordered_multimap to have several keys that are the same.
Since this is unordered you can't really hope to have any particular order, because it depends on the hash function.
If you want order in which you insert things, you need to use std::vector. Even normal maps, which are supposed to be ordered imply the comparison order, and not the order in which you insert things, for example string "AB" comes before "BB", because "A" is less than "B".
To insert without providing a value you need a set, and not a map.
The underlying structure of "unordered_" things is hashtable.
As the question says, I need to iterate over my map's elements in a certain order, that is, the traditional < order. I thought that by using integers as key, it would have been done automatically, but I was wrong. In fact, when I use a for-each loop like this:
mymap<int, Mytype*> m;
for(auto&x: m){
std::cout << x->first;
}
The keys aren't in order! Why does that happen?It's iterator's fault or maybe it's because of
the hashing function?
EDIT:now I've noticed that if I change the order of insertion it changes the result of the for-each loop too.
EDIT2: I know unordered_map is unordered. But:
"Internally, the elements in the unordered_map are not sorted in any particular order with respect to either their key or mapped values, but organized into buckets depending on their hash values to allow for fast access to individual elements directly by their key values (with a constant average time complexity on average)."(from c++reference)
So I thought I could use a particular Hash function that could give also the order(since keys are int)
From the title of your question I assume that mymap is unordered_map. Well, unordered_map is unordered. The order of entries does not follow from operator< or anything like that, and you may assume that the order is random. The map iterators reflect this internal order.
You can't get order out of an std::unordered_map, you can't really write an ordered iterator for it, unless you really hack the concept.
If you want ordered iteration, either store it in a std::map, which is ordered, but likely slower in accessing, or either copy the contents, or create indexing into a different container, sort it (or use a sorted container like std::map), and use that data. You either re-create this data each time you need it ordered, or keep the two containers in sync. For this the best choice would be to use boost::multi_index_container. Reference: http://www.boost.org/doc/libs/1_55_0b1/libs/multi_index/doc/index.html
I have a multimap containing over 5 million pairs and I need to swap the keys with the values.
unordered_multimap<int, int> edge;
Due to the large size of the container and the processes involved, I would prefer to not have to create a new multimap with the swapped pairs by iterating over each element of the map.
What would be the best way, if any, to do this in place?
The proper approach is not to do this at all, but instead to have a bi-directional map in the first place, on which you can perform lookup in either direction.
Consider looking into Boost.Bimap.
You can not do this in-place.
The map you have stored the elements based of the hash values of its key. If you want to hash on a different key (the former value) you have to rebuild the whole map or think of a different way to store the elements.
Boost.Bimap (as suggested by Lightness Races in Orbit) for example supports bidirectional unordered multimaps.
EDIT: Please note, I'm NOT asking why multimap can't contain duplicate keys.
What's the rationale behind multimap allowing duplicate key-value pairs? (not keys)
#include <map>
#include <string>
#include <iostream>
int
main(int argc, char** argv)
{
std::multimap<std::string, std::string> m;
m.insert(std::make_pair("A", "B"));
m.insert(std::make_pair("A", "B"));
m.insert(std::make_pair("A", "C"));
std::cout << m.size() << std::endl;
return 0;
}
This printed 3, which somewhat surprised me, I expected multimap to behave like a set of pairs, so I was expecting 2.
Intuitively, it's not consistent with C++ std::map behaviour, where insert does not always change the map (as opposed to operator[]).
Is there a rationale behind it, or it's just arbitrary?
Multimap only has a predicate ordering the keys. It has no method to determine whether the values are equal. Is value "A" a duplicate of value "a"? Without a second predicate for the values, there's no telling. Therefore, it doesn't even make sense to talk about duplicate values in a multimap.
If you would like a container that stores pairs, and enforces the unique-ness of both parts of the pair, look at boost::multi_index_container. It's very flexible, but takes a load of arguments as a result.
EDIT: This answer does not answer the current question anymore. I'll keep it as it is because it got upvoted a lot so it must be useful for some.
The multi in multimap stands for the fact that the same key can occur multiple times.
The standard puts no limit on the type used as value, so one cannot assume that operator==() is defined. Because we don't want the result of your code depend on whether the operator==() is defined or not, it is never used.
std::multimap is not a replacement for std::map. As you noticed, it behaves differently when the same key is inserted multiple times. If you want std::map's behaviour, use std::map.
There is also a std::multiset.
The rational: sometimes one would like to keep all old entries for the same key around as well. [TBD: Insert some example here]
Personally, I barely ever use std::multimap. If I want multiple entries for the same key, I usually rely on std::map<std::vector<T> >.
The values are allowed to be duplicates because they are not required to be comparable to each other. The container cannot do anything with the values besides copy them in. This enables types like multimap< int, my_class >.
If duplicate key-value pairs are undesirable, then use set< pair< T, U > > and use lower_bound to find the first match to a given key.
As you know, multimap allows to have multiple keys. Since it does not place any constraints on values comparability, it is unable to check, if values haven't been doubled.
If you want to have some dictionary data structure which allows for duplicate keys, but not key-value pairs, you would have to ensure that values are comparable.
Let's say we have a game of some sort, where there is 2D world of sqaure fields, and you can put items on fields. You can have multimap<Field, Item>, which will allow you to keep two identical items on the field. Items don't have to be comparable here.
My reasoning is multimap is based on the Key lookup/insertion and not on the value. So whether the value on duplicate keys is the same or not does not play a part when elements are being inserted.
23.3.2 Class template multimap
1 A multimap is a kind of associative
container that supports equivalent
keys (possibly containing multiple
copies of the same key value) and
provides for fast retrieval of values
of another type T based on the keys.
"multimap" is meant to support 'multiple' keys unlike simple "map". Since it allows multiple keys, it won't bother for their values, so it shows 3 elements in your example. The other difference is, one can not have operator [] for multimap.
A use of duplicate [map,value] pairs is to count the number of occurrences of say a word on a page of a book, be it no times, thus no entry in the multimap for that word, be it once with one entry, or more than once with the number of occurrences in multimap for make_pair(word, page_number). It was more by accident that design that I found this usage.