1

I need to insert values into std::map (or it's equivalent) to any free position and then get it's key (to remove/modify later). Something like:

std::map<int, std::string> myMap;
const int key = myMap.insert("hello");

Is it possibly to do so with std::map or is there some appropriate container for that?

Thank you.

6
  • I don't think std::map is the right choice for what you want to achieve. What are you trying to do? It's non-sense adding an object without an associated key into a dictionary. Commented Sep 22, 2011 at 9:52
  • 1
    That's not how it works... a map, being usually implemented as an RB tree, doesn't have a "default key" associated with any free slot. What exactly are you trying to achieve? Commented Sep 22, 2011 at 9:52
  • Possibly you are looking for hash function - search information about this topic. Commented Sep 22, 2011 at 9:54
  • @Matteo Italia, I need just way to frequently insert/update/remove objects within any array. I think std::map's (ordered tree?) implementation gives the way to find some unoccupied key - isn't it so? Commented Sep 22, 2011 at 9:57
  • @Slav: no, that is not how it works. there are no "unoccupied keys" in a map. When a map has no data for a certain key, it also has no data for the key itself. When you have an empty map<uint32_t> you have over 4 billion "unoccupied keys"... Commented Sep 22, 2011 at 10:03

5 Answers 5

2

In addition to using a set, you can keep a list of allocated (or free) keys, and find a new key before inserting. For a map indexed by int, you can simply take the last element, and increment its key. But I rather think I'd go with a simple std::vector; if deletion isn't supported, you can do something simple like:

int key = myVector.size();
myVector.push_back( newEntry );

If you need to support deletions, then using a vector of some sort of "maybe" type (boost::optional, etc.—you probably already have one in your toolbox, maybe under the name of Fallible or Maybe) might be appropriate. Depending on use patterns (number of deletions compared to total entries, etc.), you may want to search the vector in order to reuse entries. If your really ambitious, you could keep a bitmap of the free entries, setting a bit each time you delete and entry, and resetting it whenever you reuse the space.

Sign up to request clarification or add additional context in comments.

4 Comments

"you may want to search the vector in order to reuse entries" - or keep a stack of free indexes. Push the stack when you delete an entry, and when adding a new entry either the stack is empty (in which case push_back the vector as above), or else use an index popped off the stack.
Keeping a stack of the unused entries is a very good idea. It's both very simple to implement, and efficient in terms of runtime.
And a possible performance benefit that perhaps isn't obvious, is that using old entries in LIFO order helps keep the cache warm.
@Steve Jessop Maybe. If you find a range, and allocate within that, the recently allocated values will have good locality. And of course, using a bitmap may result in significantly less memory being used (which also helps locality) if there are a lot of empty slots. (Determining what's best for locality up front is often a real guessing game.)
1

You can add object to an std::set, and then later put the whole set into a map. But no, you can't put a value into a map without a key.

Comments

1

The closest thing to what you're trying to do is probably

myMap[myMap.size()] = "some string";

The only advantage this has over std::set is that you can pass the integer indexes around to other modules without them needing to know the type of std::set<Foo>::iterator or similar.

1 Comment

That's a clever trick. The only problem is if there are deletions; you may end up with an already used index. Something like prec(myMap.end())->first (assuming the map isn't empty) would solve that (and of course, if the application doesn't require deletions, your trick works out of the box).
0

It is impossible. Such an operation would require intricate knowledge of the key type to know which keys are available. For example, std::map would have to increment int values for int maps or append to strings for string maps.

You could use a std::set and drop keying altogether.

3 Comments

You're right in general, but in his case, the key type is int, so we know how to do it.
Yea, but that's if you only need to store small integers or don't need the key type in any case. Suppose you get some IDs with high integers from an external source and want to add one of your own (a typical use case for a map), then the simple vector approach won't work. I like your solution, but he underspecified his problem :-)
If some of the id's are imposed (and might be very large), as you say, the std::vector approach won't work. In that case, you could just use the index of the last element + 1 for a new element. Or you could iterate to find a hole (possibly caching the results of the iteration as a range, so you don't have to iterate every time). Or maybe something else---as you say, the problem is seriously underspecified.
0

If you want to achieve something similar to automatically generated primary keys in SQL databases than you can maintain a counter and use it to generate a unique key. But perhaps std::set is what you really need.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.