What is the quickest way of inserting/updating std::unordered_map elements without using an if?

StackOverflow https://stackoverflow.com/questions/19197799

  •  30-06-2022
  •  | 
  •  

Question

I currently have lots of code which looks like this:

std::unordered_map<int,int> my_dict;
.
.
.
// If the key does exist in the dictionary
if(my_dict.count(key) == 1){
    my_dict[key] = value;
}

// If its a new key
else{
    my_dict.insert(std::make_pair(key,value));
}

Is there any way I can speed this up by just overwriting the value every time?

Was it helpful?

Solution

You just do (for map and unordered_map)

mydict[key]=value;

OTHER TIPS

I think it might be fastest like this:

auto it = my_dict.find(key);
if( it != my_dict.end() ) {
    it->second = value;
}
else {
    my_dict.insert(std::make_pair(key,value));
}

that way you don't modify the structure of the unordered_map if the key already exists and you only have one lookup.


Another option in case you don't need/access value afterwards:

my_dict[key] = std::move(value);

This might be better in cases when the assignment of value is expensive and benefits from move-semantics.

To update for C++17, you can use:

std::unordered_map::insert_or_assign()

http://en.cppreference.com/w/cpp/container/unordered_map/insert_or_assign

All of your map functions perform a search, so you always search the map twice regardless of the key being present or not. You can leverage the fact that insert retrieves whether the insertion took place (key did not exist) or not (key exists) and act accordingly:

std::unordered_map<int,int> mydict;
bool inserted = false;
auto position = mydict.end();
std::tie(position, inserted) = mydict.insert({key, value});
if (inserted) {
  pos->second = value;
}

This would be equivalent to mydict[key] = value, because we are assigning the new value no matter what. For types where default construction is cheap I would go with operator[] instead, if that's the only thing you need to do with the map.

All insert, emplace and operator[] can perform an additional construction of value_type in different situations: insert and emplace do this before the insertion takes place and operator[] default constructs the mapped value when key is not present. Thus, they are not ideal for types whose construction/copy/move is expensive (std::thread, very large std::array...). In this case, it is more appropriate to use try_emplace instead (C++17):

std::unordered_map<int, expensive_type> mydict;
bool inserted = false;
auto position = mydict.end();
std::tie(position, inserted) = mydict.try_emplace(key, expensive_constructor_args);
if (!inserted) {
  // no expensive_type has been constructed
  // pos->second references the existing value
}

Usually you avoid extra typing by means of defining a function that saves you from typing the same again. If you don't have access to C++17's insert_or_assign(), you can implement something like this yourself:

bool InsertOrAssign(std::unordered_map& m, int key, int value) {
  // Your code or one of the suggested answers goes here
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top