C++中等价于Java中Map getOrDefault()方法的是什么?

9

Java的getOrDefault是一种很好的构造,可以在一行代码中访问映射值或者不存在时的起始点。我在C++的映射参考文献中没有看到类似的东西。是否存在这样的东西,还是要自己构建?

如果映射中存在对象,则需要更新它们,否则需要创建新对象。使用getOrDefault,我可以在默认情况下构造对象,或者访问它(如果它已经存在)。

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

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


你想返回一个引用还是一个值? - L. F.
这里是同样的问题 https://dev59.com/I1cO5IYBdhLWcg3wsTrM,只不过默认值是一个可选项。 - Jerry Jeremiah
如果map.at()函数找不到元素,它会抛出一个异常。但是,如果使用map []运算符,则会插入一个默认构造的值(如果不存在)并返回该值。因此,您可能想要使用auto v = (m.find(k)!=m.end()?m[k]:"default"),但与Elvis运算符相比,这太长且复杂了。 - Jerry Jeremiah
我应该多说一些关于我的意图。如果地图中存在对象,我会对其进行更新,但如果不存在,则构建新对象。通过使用getOrDefault,我可以在默认侧构建对象,或者访问已存在的对象。 - Evan
我从不想要一个可选项,我要么想要一个根据当前情况传递一些参数的新构造对象,要么就是已经在映射中的对象。 - Evan
显示剩余5条评论
6个回答

4

我在地图中有一些对象,如果它们存在,我会更新它们,但如果不存在,我会新建一个。使用 getOrDefault 方法,我可以在默认侧构造对象或访问它(如果它存在)。

使用 emplace

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

emplace(就地构造)在键不存在时插入一个新元素,并返回一个由指向元素的迭代器(已插入或已存在)和一个bool值组成的pair,表示是否进行了插入操作。


谢谢,那正是我在寻找的函数。它就在我的眼前,但我错过了它。 - Evan
2
@Evan 这真的是你想要的吗?getOrDefault 是获取 默认值,而不是获取或插入然后获取。也就是说,getOrDefault 从不向映射中插入任何内容。 - Barry
@Barry,这绝对是真的,但在这种情况下,我的意图是在地图中插入我构建的对象(如果它不存在)并在其存在时更新它。获取引用以及一个布尔值指示它是否存在使我能够做我想做的事情。这是我对getOrDefault最常见的使用情况。 - Evan

4

我在尝试使用C++中的哈希表解决LeetCode Single Number问题时,发现没有可替代getOrDefault方法的直接替代方案。但是,我可以使用operator[]访问无序映射(unordered_map)的元素。

请参考http://www.cplusplus.com/reference/unordered_map/unordered_map/operator[]/

如果键(key)存在于映射中,则operator[]将返回其对应的引用。

如果键不存在于映射中,则operator[]将添加键并将其值设置为0。

当您尝试增加已经存在的键的值或添加新的键到映射中时,这将非常有用。

例如,我在C++中使用了以下代码:

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

这是Java中以下代码的替代方案:

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


1
L.F的答案是使用map::emplace,如果map的值类型不需要构造函数,则这是正确的方法,因为每次调用构造函数都会像下面的示例一样被调用。
C++17带有略微不同的insert_or_assign
可能最有效的解决方案是像这样做:
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);
}

下面是一个示例来说明它的工作原理:
#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;
}

这将会产生

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

这表明insert_or_create不会调用Element的构造函数。

我不确定为什么这样的函数没有出现在std::map接口中,因为它非常有用。


感谢您重新关注这个问题并添加新的信息。+1 - Evan

0
  • Java

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

  • C++中的等效代码

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

0

我认为下面是你要找的内容:

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

就像下面的示例中使用的那样。

    #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++; 

0
也许我误解了你的问题,但这就是map的工作方式。
map<int, string> m;
string s = m[3];

将会把s设置为一个默认构造的string

当你使用operator[]在map中查找一个键时,它总是会返回一个值。如果该键不存在于map中,则会插入该键,并带有一个默认构造的值。

如果您想要这种行为,但使用不同的值(而不是默认构造的值),那么您可以使用emplace,就像L.F.所建议的那样。


对,我需要一个非默认构造的东西。L.F.的答案正是我在寻找的。 - Evan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接