如何在 unordered_map 中设置值并确定是否添加了新键

How to set a value in an unordered_map and find out if a new key was added

我如何有效地和惯用地在 unordered_map 中设置一个值来查明是否添加了新键:

#include <unordered_map>
#include <string>

int main() {
  auto map = std::unordered_map<std::string, int>{{"foo", 1}, {"bar", 2}};

  map["foo"] = 3;
  // how to find out if a new key was added?
}

我不能直接使用 insert(),因为我想覆盖已有的值,而 insert 不会这样做。我不能直接使用 operator[],因为它没有提供有关是否添加了新密钥的信息。

出于性能原因,我想避免在地图中进行两次搜索。

我在其他地方看到的一个技巧是获取引用并检查该值是否是默认构造的:

auto& value = map["foo"];
if(value == 0) {
    // am inserting a new key
}
value = 3;

但我确实可以在我的地图中使用默认构造值,因此默认构造值并不是新键的良好指示。

到目前为止我能想到的最好的是:

auto size_before = map.size();
map["foo"] = 3;
if (map.size() > size_before) {
    // am inserting a new key
}

这看起来很难看,它假设获得 unordered_map 的大小很便宜(是吗?)。

看起来 unordered_map::insert_or_assign 可能是我祈祷的答案,但遗憾的是它出现在 C++17 中,所以我可能再过 5 年左右才能使用它。这似乎是一件很常见的事情,所以我认为目前必须有一种合理的方式来做这件事。

您可以使用std::unordered_map::insert和测试结果。

有辅助功能:

template<typename Map, typename T>
std::pair<typename Map::iterator, bool>
insert_or_assign(Map& m, const typename Map::key_type& k, const T& t)
{
    auto p = m.insert({k, t});
    if (!p.second) {
        // overwrite previous value
        p.first->second = t;
    }
    return p;
}

然后

auto p = insert_or_assign(map, "foo", 3);
if (p.second) {
    // inserted
} else {
    // assigned
}

Live Demo

您可以使用 insert() 成员函数,它将 return 一个 pair 其中 return 一对由迭代器组成的插入元素(或元素阻止了插入)和一个表示插入是否发生的布尔值。看起来像

if (map_name.insert(some_value).second)
    value was inserted
else
    value was not inserted

如果你需要在没有插入元素的时候修改map中的值那么你可以使用

auto ret = map_name.insert(some_value);
if (!ret.second)
    *(ret.first) = some_value;

编辑:更新

从 C++17 开始,std::mapstd::unordered_map 具有成员函数 insert_or_assign()。如果 key/value 对不在映射中,此函数将插入到容器中,如果键已存在,则将覆盖容器中的现有值。该函数将 return 一个 std::pair 包含一个指向 inserted/updated 元素的迭代器和一个 bool 信号(如果有插入)。如果有插入那么它将是 true 否则 false

std::unordered_map<std::string, int> foo = { { "foo", 1 },{ "bar", 2 } };
auto ret = foo.insert_or_assign("foo", 3);
if (ret.second)
    std::cout << "foo was inserted";
else
    std::cout << "foo already exist.  new value: " << ret.first->second;

输出:

foo already exist.  new value: 3

我目前找不到支持 C++17 的在线编译器,我可以在上面分享代码,但你可以 运行 它 here 或 Microsoft Visual Studio 2015