quick accessing to element of std::map - c++

Do you know if it is any difference in performance when I access a std::map element using find or operator []?
One returns an iterator and the other a const ref to the object.
Which one might be quicker becuase of all of the behind the scene of the STL?

When you use [] on a key that doesn't exist, the default element will be inserted. This default element depends on your map definition (for example, for an int it will be a zero).
When you use find, there is no "automatic" insertion, so it can be quite faster if you often search for keys that does not exist.

find() is O(n). operator [] is O(1). Therefore the latter is (usually) faster.

Related

Why does map have operator[] but set does not?

std::map and std::set seem very similar to me (but in their use and in their description), so I do not understand why std::set does not implement its version of operator[].
I suspect it is linked to the fact that elements in a std::set are const, but even then why not implement an operator[] that either returns a const reference or creates a new element ?
Depending on the answer to this first question, would it be possible/a good idea to create a version a std::set that implements operator[] ?
Well, std::map<Key, Val> maps Key to Val.
That is, m[key] yields a reference to the val. You have the key, and want to find the associated value (or associate a value with that key).
In a std::set<Elem>, the element would be its own key. So, the only thing you could get back would be the thing you already have. What would you use this operation for?
A set is not for mapping one thing to another - that's what a map does. A set is for recording whether or not an element belongs to some collection. So, the only sane thing to use it for is checking, given some element, whether or not that element is a member of the set. We do this with s.find(elem) != s.end() or, from c++20, s.contains(elem).
The fact that the set is described as std::set<Key, ...> may be a source of confusion - I suspect this is just because it's used for searching in the same way as the map key.
You could in principle choose to characterize a set as map<Elem, bool>, but unless you want to really store the bool (which would be wasteful), the element access and iterator semantics would be a bit hairy. That is, it would be mathematically accurate and consistent, but either wasteful or complicated in implementation.
In fact a map is an associated array only instead of integer indices it uses keys as indices.
As ordinary arrays have the subscript operator then and maps have a similar subscript operator.
On the other hand, sets are not associated arrays. In sets keys are their data. So a question arises what should an expression like this set[key] return? There is no greate sense to return itself and moreover when the returned value may not be changed.
It would not be very useful to use set if you want to know the key of an element, since a map would be more useful, which does have the function you require.

Speeding up positional access to a std::map

I find myself in a situation where I have to access a std::map by position. Since std::advance(iter, position) is slow af, I want to add a second data structure to speedup this operation.
My idea: Maintain a vector for every key-value pair in the map. Then access the vector by position, vector[position]->second.
When erasing/inserting a new element I obviously have to remove the iterator from the vector. But besides that the iterator-preserving properties of std::map seem to be sufficient.
Question: Is this a good idea?
Alternative: Maintain a vector of map::keys. Then access vector by position an use the key to lookup the value in the map,map[vector[position]]. Is this smarter?
If iteration through the map is your primary performance issue, then you should be using a flat_map (not as of yet part of the standard, but Boost has a decent one). If you don't have one of those, just use a vector<pair> that you keep sorted using the same comparison function you would have used in your map.
You can use std::lower_bound as the equivalent function for being able to find a value by its key. You can also use the iterator returned from std::lower_bound as the position for doing a single-element insertion of a new element. Everything else works just like any other vector; simply keep it sorted and you'll be fine.
std::map search, removal, and insertion operations have logarithmic complexity. The same complexity can be achieved by a sorted std::vector
Don't use a map, but a vector. Keep it sorted by key. Binary search by key is logaritgmic. Access by position is the fastest.
The only drawback is that inserting and removing needs memory reallocation. You may test its performance and consider if it's worth.

Efficiently check element exists in map c++

I am aware of the .find() method to check whether or not an element exists in a map. But suppose i have a map -- map -- with about 2000 elements and I want to add an element to the list. I first have to check if the element exists. Isn't it a bit inefficient to use .find() because the iterator has to 'iterate' over element in the map? Is there a more efficient and less time consuming way to check whether or not an element exists in a map?
std::map has logarithmic look-up complexity, so you won't have to visit all the elements to determine whether an element is in the map.
On the other hand, you can make the look-up and insert operation more concise by using std::map::emplace or std::map::insert:
auto [iter, ok] = the_map.emplace(the_key, a_value);
if (ok) {
// element was not in map so got inserted
}
This assumes that a_value either already exists, or that it is not an issue to construct it even if it does not end up being inserted into the map.
If you really need to use 'find and insert' idiom (and that could be because you can't construct a value object for the same key twice under any circumstances) and you are concerned about twice logarithmic complexity, you'd have to use std::map::equal_range.
The way to do this would be first use equal_range with your key, and than use one of returned iterators as a hint to insert.

What container to store unique values with no operator< defined

I need to store unique objects in a container. The object provides a operator== and operator!= (operator< nor operator>).
I can't use std::set, as it requires a operator<.
I can't use std::unordered_set as it requires a hash function and I have none. Let's say I can't write one considering my object type (or I'm lazy).
Am I really forced to use a std::vector and make sure myself that items does not get duplicated in the container (using std::find which uses operator==)?
Is there really no container that could be used to store unique items only using the operator==?
There's indeed no standard container, and that's because it would be inefficient. O(N), to be precise - exactly the brute force search you imagine.
Both std::set<T> and std::unordered_set<T> avoid a brute-force search by taking advantage of a non-trivial property of T. Lacking either property, any of the existing N members of a container could be equal to a potential new value V, and you must therefore compare all N members using operator== repeatedly.
"Let's say I can't write a hash function considering my object type (or I'm lazy)."
Well, you're lazy, but I'll write one for you anyway : template<typename T> size_t degenerate_hash(T) { return 0; }.
Of course, this means you get O(N) performance because every value collides with every other value, but that was the best possible outcome anyway.
Use a std::vector and before you std::vector::push_back or std::vector::insert use first std::find to check whether the element already exists in the vector.
Or at the end of all insertions use std::unique in combination with std::vector::erase to remove duplicates.

c++ std::map question about iterator order

I am a C++ newbie trying to use a map so I can get constant time lookups for the find() method.
The problem is that when I use an iterator to go over the elements in the map, elements do not appear in the same order that they were placed in the map.
Without maintaining another data structure, is there a way to achieve in order iteration while still retaining the constant time lookup ability?
Please let me know.
Thanks,
jbu
edit: thanks for letting me know map::find() isn't constant time.
Without maintaining another data structure, is there a way to achieve in order iteration while still retaining the constant time lookup ability?
No, that is not possible. In order to get an efficient lookup, the container will need to order the contents in a way that makes the efficient lookup possible. For std::map, that will be some type of sorted order; for std::unordered_map, that will be an order based on the hash of the key.
In either case, the order will be different then the order in which they were added.
First of all, std::map guarantees O(log n) lookup time. You might be thinking about std::tr1::unordered_map. But that by definitions sacrifices any ordering to get the constant-time lookup.
You'd have to spend some time on it, but I think you can bash boost::multi_index_container to do what you want.
What about using a vector for the keys in the original order and a map for the fast access to the data?
Something like this:
vector<string> keys;
map<string, Data*> values;
// obtaining values
...
keys.push_back("key-01");
values["key-01"] = new Data(...);
keys.push_back("key-02");
values["key-02"] = new Data(...);
...
// iterating over values in original order
vector<string>::const_iterator it;
for (it = keys.begin(); it != keys.end(); it++) {
Data* value = values[*it];
}
I'm going to actually... go backward.
If you want to preserve the order in which elements were inserted, or in general to control the order, you need a sequence that you will control:
std::vector (yes there are others, but by default use this one)
You can use the std::find algorithm (from <algorithm>) to search for a particular value in the vector: std::find(vec.begin(), vec.end(), value);.
Oh yes, it has linear complexity O(N), but for small collections it should not matter.
Otherwise, you can start looking up at Boost.MultiIndex as already suggested, but for a beginner you'll probably struggle a bit.
So, shirk the complexity issue for the moment, and come up with something that work. You'll worry about speed when you are more familiar with the language.
Items are ordered by operator< (by default) when applied to the key.
PS. std::map does not gurantee constant time look up.
It gurantees max complexity of O(ln(n))
First off, std::map isn't constant-time lookup. It's O(log n). Just thought I should set that straight.
Anyway, you have to specify your own comparison function if you want to use a different ordering. There isn't a built-in comparison function that can order by insertion time, but, if your object holds a timestamp field, you can arrange to set the timestamp at the time of insertion, and using a by-timestamp comparison.
Map is not meant for placing elements in some order - use vector for that.
If you want to find something in map you should "search" by the key using [the operator
If you need both: iteration and search by key see this topic
Yes you can create such a data structure, but not using the standard library... the reason is that standard containers can be nested but cannot be mixed.
There is no problem implementing for example a map data structure where all the nodes are also in a doubly linked list in order of insertion, or for example a map where all nodes are in an array. It seems to me that one of these structures could be what you're looking for (depending which operation you prefer to be fast), but neither of them is trivial to build using standard containers because every standard container (vector, list, set, ...) wants to be the one and only way to access contained elements.
For example I found useful in many cases to have nodes that were at the same time in multiple doubly-linked lists, but you cannot do that using std::list.