Evan
Evan

Reputation: 2556

C++ equivalent to Java Map getOrDefault?

Java's getOrDefault was a nice construct for one line access to a map value or the starting point if one does not exist. I do not see anything in the map reference in C++ with a parallel. Does something exist or is it build your own?

I have objects in the map that I would update if they exist, but construct new if they do not. With getOrDefault, I could construct the object on the default side, or access it if it exists.

http://www.cplusplus.com/reference/map/map/

https://www.geeksforgeeks.org/hashmap-getordefaultkey-defaultvalue-method-in-java-with-examples/

Upvotes: 10

Views: 10843

Answers (6)

Daniel
Daniel

Reputation: 1610

The answer of L.F is using map::emplace is the way to go if the map's value type does not require a constructor, as each time the constructor is called as shown in the example below.

C++17 comes with insert_or_assign which is slightly different.

Probably the most efficient solution is to do something like this:

template <class M, class Vp>
std::pair<typename M::iterator, bool> insert_or_create(M& map, typename M::key_type&& k, Vp&& v) {
    auto p = map.lower_bound(k);
    if (p != map.end()) {
        return std::make_pair(p, false);
    }
    return std::make_pair(map.emplace_hint(p, std::move(k), std::forward<Vp>(v)), true);
}

Here is an example that shows how it works:

#include <sstream>
#include <iostream>
#include <string>
#include <map>

class Element {
public:
    Element(int value) : value(value) {
        std::cout << "Element ctor value = " + std::to_string(value) << std::endl;
    }
    int value;
};


template <class M, class Vp>
std::pair<typename M::iterator, bool> insert_or_create(M& map, typename M::key_type&& k, Vp&& v) {
    auto p = map.lower_bound(k);
    if (p != map.end()) {
        return std::make_pair(p, false);
    }
    return std::make_pair(map.emplace_hint(p, std::move(k), std::forward<Vp>(v)), true);
}

int main(int argc, char **argv) {
    std::map<int, Element> map;

    auto& e1 = *map.emplace(1, Element(1)).first;
    std::cout << "Element in map: " << std::to_string(e1.second.value) << std::endl;
    auto& e11 = *map.emplace(1, Element(11)).first;
    std::cout << "Element in map: " << std::to_string(e11.second.value) << std::endl;

    auto e2 = *map.insert_or_assign(2, 2).first;
    std::cout << "Element in map: " << std::to_string(e2.second.value) << std::endl;
    auto e22 = *map.insert_or_assign(2, 22).first;
    std::cout << "Element in map: " << std::to_string(e22.second.value) << std::endl;

    auto e3 = *insert_or_create(map, 3, 3).first;
    std::cout << "Element in map: " << std::to_string(e3.second.value) << std::endl;

    auto e33 = *insert_or_create(map, 3, 33).first;
    std::cout << "Element in map: " << std::to_string(e33.second.value) << std::endl;
}

which produces

Element ctor value = 1
Element in map: 1
Element ctor value = 11       <-- calling ctor, not optimized away   
Element in map: 1             <-- still old value in map as expected 
Element ctor value = 2
Element in map: 2
Element ctor value = 22       <-- calling ctor  
Element in map: 22            <-- new value assigned to key 2, as expected
Element ctor value = 3
Element in map: 3             <-- ctor not called as wanted!!!!!!   
Element in map: 3

This shows that insert_or_create does not call the constructor of Element.

I am not sure why such a function is not in the std::map interface as it is pretty useful.

Upvotes: 1

gov
gov

Reputation: 75

I think below is what you are looking for

mymap.emplace(key, DEFAULT_VALUE).first->second = value;

like used in below sample

    #define DEFAULT_VALUE 0
    map<int,int> mymap;
    mymap[1] = 1;
    // mymap is having some entries.

    // will increment existing element.
    mymap.emplace(1,DEFAULT_VALUE).first->second++; 

    // will insert a new element with key 2 and value as default(0), than increment it.
    mymap.emplace(2,DEFAULT_VALUE).first->second++; 

Upvotes: 0

Amit Singh
Amit Singh

Reputation: 127

  • Java

    map.put(sum, map.getOrDefault(value, 0) + 1);

  • Equivalent code in c++

    auto it1 = map.find(value);
    if (it1 != um.end())
        map[value]++;
    else
        map[value] = 1;

Upvotes: 0

Robert Kim
Robert Kim

Reputation: 51

I got here when I was trying to figure out a way to solve LeetCode Single Number question using HashTable in C++.

There is no direct alternative to getOrDefault which is used in Approach 2 and Java, but I was able to use operator[] to access an element of an unordered_map. See http://www.cplusplus.com/reference/unordered_map/unordered_map/operator[]/

If the key exists in the map, then the operator[] will return the reference to its mapped value.

If the key does not exist, then the operator[] will add the key to the map with a value 0.

This could be useful when you are trying to increment a value of a key if it already exists or add a new key to the map if it does not.

For example, I used the following in C++

for (int i : nums) { hash_table[i] = hash_table[i] + 1; }

which is an alternative to the following in Java

for (int i : nums) { hash_table.put(i, hash_table.getOrDefault(i, 0) + 1); }

Upvotes: 4

Marshall Clow
Marshall Clow

Reputation: 16680

Maybe I'm misunderstanding what you're asking, but that's how map works.

map<int, string> m;
string s = m[3];

will set s to a default-constructed string.

When you use operator[] to look up an key in a map, it will always hand you back an value. If the key doesn't exist in the map, it will insert it, with a default constructed value.

If you want this behavior, but with a different value (not a default-constructed value), then you could use emplace, as L.F. has suggested.

Upvotes: 0

L. F.
L. F.

Reputation: 20609

I have objects in the map that I would update if they exist, but construct new if they do not. With getOrDefault, I could construct the object on the default side, or access it if it exists.

Use emplace.

auto& element = *map.emplace(key, value).first;

emplace inserts a new element if the key is not present, and returns a pair consisting of an iterator to the element (inserted or already existent) and a bool value indicating whether insertion took place.

Upvotes: 5

Related Questions