My goal is to write a class that works like a unordered_map but keeps the insertion order of the elements while still allowing O(1) lookup by key.
My approach is as follows:
// like unordered_map but keeps the order of the elements for iteration
// implemented as a vector with a unordered_map for constant time lookups
// consider if element removal is needed, since it is O(N) because of the use of a vector of elements
template <typename KEY, typename VAL>
struct ordered_map {
struct KeyValue {
KEY key;
VAL value;
};
std::vector<KeyValue> elements; // KeyValue pairs in order of insertion to allow iteration
std::unordered_map<KEY, int> indexmap; // key -> index map to allow O(1) lookup, is there a way to avoid the redundant key?
//...
}
But I have the problem that in my approach I want to lookup into the indexmap using the keys which are stored 'externally' to it (in the elements vector based on the index value in the map).
std::sort
for example allows passing in a comparator, but unordered_sap does not seem to have anything similar.
I could not really find anything online about how to accomplish this, but I might be searching with the wrong terms.
I this approach at all supported by the stl?
Or do I need to resort to storing the Key twice , which I would like to avoid as keys can be heap objects like std::strings for example.
EDIT: unordered_map instead of unordered_set which does not work
My goal is to write a class that works like a unordered_map but keeps the insertion order of the elements while still allowing O(1) lookup by key
... and without duplicating the key, in cases it's large and/or expensive.
So, you want two things:
std::unordered_map
(no duplicate keys, or you would/should have asked for std::unordered_multimap
)You have chosen to implement the associative lookup using a sequential container, and the sequential index using an associative container. I don't know why, but let's try the more natural alternative:
std::unordered_map<Key, SomeValueType>
std::vector<SomeValueType*>
The remaining blank is SomeValueType
: it could just be VAL
, but then we need to do extra work to fix the sequential index whenever we erase something. We could make it std::pair<VAL, size_t>
instead, so it can store the index i
of the iterator we'll need to remove on erasure. The down side is that on top of moving all i+1..N
vector elements down one, we also need to update the index value for each of those map elements.
If you want to preserve constant-time erasure, you probably need the sequential index to be a std::list<SomeValueType*>
, and to keep a std::list::iterator
in your map element. Actual linear iteration will be slower on a linked list than a vector, but you get the same complexity when erasing elements.
NB. The sequential index does need to store pointers rather than iterators - I originally forgot the invalidation behaviour for unordered maps. However, if you want access to the key, they can obviously be std::unordered_map<key, SomeValueType>::value_type
pointers... I just wrote out the shorter alternative.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.