For an std::map, can i always trust begin() to return the element with the smallest key according to comparison operators for the type, when iterating?
In other words...
Will std::map<Key, SomeClass>::iterator smallestKeyIt = someMap.begin(); give me the pair in the map with the smallest key?
Is this the ordering that is quaranteed for an std::map or can i configure it somehow? My understanding is that the underlaying tree structure is kept ordered when performing operations such as adding and removing elements.
can i always trust begin() to return the element with the smallest key according to comparison operators for the type?
Yes.
Is this the ordering that is quaranteed for an std::map or can i configure it somehow?
Yes. And you can configure the behaviour of comparing by specify the comparator. (The default one is std::less.)
From cppreference:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
std::map is a sorted associative container that contains key-value
pairs with unique keys. Keys are sorted by using the comparison
function Compare. Search, removal, and insertion operations have
logarithmic complexity. Maps are usually implemented as red-black trees.
std::map is defined as:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
You can use your specialized Compare to configure how the entries of a map are ordered. For example, if you use:
std::map<int, double, std::greater<int>> myMap;
then, the first entry in myMap will have the largest key.
Related
I am writing a custom implementation of hash map (similar to std::unordered_map) and one of the steps is the following:
There is a std::list of <KeyType, ValueType> pairs, which contains all the objects stored in the hashtable. I need to build an iterator that would run over the list, hovewer, it must address not std::pair<KeyType, ValueType> but std::pair<const KeyType, ValueType> (so that the user can't change the keys), so I can't use the built-in list iterator.
So how it can be done?
Thank you
In case of unordered_map we define the hash and pred functors whenever we are using user-defined keys.
The template syntax for a map is as follows:
template < class Key, // map::key_type
class T, // map::mapped_type
class Compare = less<Key>, // map::key_compare
class Alloc = allocator<pair<const Key,T> > // map::allocator_type
> class map;
In case of map there is no hash and pred functors option. Do we never have collisions in case of map. If collisions happen then why don't we have the hash and pred functors as in unordered_map?
Am I missing something here?
std::map and std::unordered_map are two different types of containers that both provided key-value pair mapping. How they do that though is completely different.
std::map uses a tree structure for its implementation. Typically this is an RBTree but any tree that can guarantee worst case O(logN) operations will work. This means it only needs to have a comparison operator for the key type since you can get total ordering and check for equality with a comparator that implements a strict weak ordering. This means you'll never have a hash collision since you aren't using a hash.
std::unordered_map is based on a hash table implementation. Since it hashes the key, you need a hash operator. You also need a comparison operator since two values could hash to the same value (hash collision). Without the comparison operator you would not be able to tell if the duplicate hash is really a duplicate item.
std::map is not a hash table and thus doesn't use a hash function. Instead, it requires operator< for ordering the values contained in the map.
I'm trying to keep information in std::map. But I have a problem with find:
typedef map<string, string, equal_to<string>> MAP_STRING2STRING;
....
MAP_STRING2STRING map;
MAP_STRING2STRING::const_iterator iter;
When I try to find key, I get the following error:
iter = map.find(key);
What am I doing wrong?
This error appears only when I have something in map.
Your map has the wrong kind of comparison functor. You need strict weak ordering (less-than or greater-than type comparison), not equality. You can just omit the comparison functor parameter and use a less-than comparison for std::string. This implements strict weak ordering via a lexicographical comparison of strings:
typedef map<string, string> MAP_STRING2STRING;
This is equivalent to
typedef map<string, string, less<string> > MAP_STRING2STRING;
Internally, the map uses the strict weak ordering comparison to both order itself, and determine whether two keys are the equal.
The third template parameter allows you to instantiate a map with a custom ordering criterion. For example, this would create a map with the reverse ordering of the one above:
typedef map<string, string, greater<string> > MAP_STRING2STRING;
If you want a simple map from std::string to std::string, just use map<string, string> (drop that equal_to<string> bogus "comparator").
Moreover, since you have a variable named "map", this can cause conflict with the STL map class. Either change the variable name (e.g. call it myMap), or use the std:: namespace prefix for std::map class:
typedef map<string, string> MAP_STRING2STRING;
....
MAP_STRING2STRING myMap;
In addition, since from the error message you are using VS2010, you can use the convenient auto C++11 keyword, to avoid the "clutter" of MAP_STRING2STRING::const_iterator, and just use:
auto iter = myMap.find(someKey);
I'd like to do some set intersection operations on the keys of a std::map<> instance without duplicating the keys into a std::set<> beforehand.
It's not documented in the API, but is there any way in O(1) time to extract the keys from a std::map<> and put them into a std::set<>?
Create an iterator adapter that returns first for the map and use it with set_intersection.
Firstly no, there is not an O(1) operation to get a set of keys from a map. Constructing the set would have to be Omega(n).
However, you can do the set_intersection you want directly on the map and the other range, whatever that is. Untested code:
template <typename InputIterator, typename Map, typename OutputIterator>
void map_set_intersection(InputIterator first, InputIterator last, const Map &m, OutputIterator o) {
std::set_intersection(
first, last,
m.begin(), m.end(),
o,
KeyCompare<typename Map::value_type, typename std::iterator_traits<InputIterator>::value_type>()
);
}
All the magic is in the KeyCompare type:
template <typename MapValue, typename SetValue>
struct KeyCompare {
bool operator()(const MapValue &lhs, const SetValue &rhs) {
return lhs.first < rhs;
}
bool operator()(const SetValue &lhs, const MapValue &rhs) {
return lhs < rhs.first;
}
};
std::set_difference is defined to copy values from the first range specified, which in this case is the range defined by the iterators. It can do that provided that it can compare values in the first range with values in the second range in either order (which it can, thanks to KeyCompare). The two ranges don't need to have the same type, so you don't need a set of keys from the map.
If you're already using the 6-parameter form of set_intersection, then you need to change KeyCompare so that after taking the key out of the map entry, it uses your comparator instead of <.
If you're using a custom comparator or overloaded operator< and the value type of the iterator range is the same as the value type of the map, then this doesn't work. KeyCompare can no longer use the types to work out which order the arguments have been provided and hence which one it should extract the first out of. I don't think this is a very common scenario, but I also don't see an escape from it using this approach. You'd need Crazy Eddie's solution, adapt the map iterator. Which amounts to this question: Iterate keys in a C++ map
You can do an O(N) set intersection on your map. Remember that when you iterate through a map, the keys are in order, so you can use an algorithm very much like a merge operation...
All you do is maintain an iterator into each map, and increment the one whose key is smallest. If the keys are equal, you can add to a deque, list or vector (and in that case, you would increment both iterators). As soon as one of the iterators reaches the end of its map, you are done.
C++, using Visual Studio 2010. A question about why a user-defined trait of hash_map actually requires total ordering.
I have a simple structure, say FOO, which only has a number of integers. I'd like to use hash_map, which is a hash table whose keys are unordered, to store the structure of FOO. I just need a fast searching of its associated value, so this is a right choice: hash_map<FOO, int32_t>.
However, I need to implement my own hash function and some compare functions for FOO. Here is the definitions of hash_map, taken from MSDN:
template <
class Key,
class Type,
class Traits=hash_compare<Key, less<Key> >,
class Allocator=allocator<pair <const Key, Type> >
>
class hash_map
It turned out that I needed to implement hash_compare functors:
template<class Key, class Traits = less<Key> >
class hash_compare
{
Traits comp;
public:
const size_t bucket_size = 4;
const size_t min_buckets = 8;
hash_compare( );
hash_compare( Traits pred );
size_t operator( )( const Key& _Key ) const; // This is a hash function
bool operator( )( // This is an ordering function
const Key& _Key1,
const Key& _Key2
) const;
};
Here is the detailed description of the bool operatod() from MSDN:
For any value _Key1 of type Key that precedes _Key2 in the sequence and has the same hash value (value returned by the hash function), hash_comp(_Key2, _Key1) is false. The function must impose a total ordering on values of type Key.
The function supplied by hash_compare returns comp(_Key2, _Key1), where comp is a stored object of type Traits that you can specify when you construct the object hash_comp. For the default Traits parameter type less, sort keys never decrease in value.
It was easy to write the hash_compare class for FOO. This question is not for asking how to implement a class. However, it's not straightforward for me that why they have the default trait parameter as less<key> and require total ordering.
hash_map is an unordered data structure. So, I thought that it would be sufficient to have equal_to or not_equal_to instead of less or greater. However, the description of MSDN explicitly states that keys are ordered, which confuses me.
Did I misunderstand the definition of hash_map? Why STL's hash_map actually require orders of its key?
For any value _Key1 of type Key that precedes _Key2 in the sequence and has the same hash value (value
returned by the hash function), hash_comp(_Key2, _Key1) is false. The function must impose a
total ordering on values of type Key.
A total ordering of keys with the same hash value guarantees a total ordering of keys which hash to the same bucket.
That provides the opportunity for a more efficient implementation of search for a key within a particular bucket - e.g. Θ(log n) binary search is possible. If there is no such guaranteed ordering, the worst case (many different keys which are all in the same bucket because they all hash to the same value) is Θ(n).
hash_map that you are looking at is a Microsoft extension that came in in VS2003 and is actually now in stdext in Visual C++ - it's not part of the STL.
std::unordered_map is the official STL version of an associative container with value access by hashable key - the predicate on that is for equality, as you expected.
template<class Key,
class Ty,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<std::pair<const Key, Ty> > >
class unordered_map;
The exact requirements on hash_map vary with the implementation, and some of them (as you've seen) don't make a whole lot of sense. That's part of why they decided not to include a hash_map (or hash_*) in TR1 and/or C++0x. Instead, they have unordered_[multi](map|set), which requires only equal_key, not operator<.
Bottom line: unless you have a truly outstanding reason to do otherwise, use unordered_map instead of hash_map.