插入无序地图的更快方法

时间:2019-04-10 08:59:18

标签: c++

我有一张无序的地图,就像:

std::unordered_map < std::string, std::vector<std::string> > m_unordered_map;

在其中插入值时,以下哪一项会更快,为什么?

方法1(骨架):

std:string key = "key";
for (int i = 0; i < n; ++i) {
  std::string value = std::to_string(i); // Value would be acually an big string.
  m_unordered_map[key].push_back(value1);
}

方法2(骨架)

std:string key = "key";
std::vector<std::string> vec;
for (int i = 0; i < n; ++i) {
  std::string value = std::to_string(i); // Value would be acually an big string.
  vec.insert(value);
}
m_unordered_map.insert({key, vec});

或者有更好的方法吗?

2 个答案:

答案 0 :(得分:4)

假设您事先知道一些事情的关键,那么您拥有的第二个版本会更好,因为它避免了在每次迭代上进行地图查找。

您可以通过移动而不是复制字符串和向量来进一步改进它。例如

std::string key = "key";
std::vector<std::string> vec;
for (int i = 0; i < 10; ++i) {
    vec.emplace_back(std::to_string(i));
}
m_unordered_map.emplace(key, std::move(vec));

通常:

  • 使用unordered_map之类的键尤其是访问std::string仍然相当缓慢。对于“命中”,将花费键长度上的O(n)哈希值和O(n)字符串比较。哈希表本身不只是O(1)。如果您只能访问它一次,则可能保留一个更快的迭代器/引用(请检查迭代器失效规则。insert可能会使unordered_map中的其他迭代器失效,因此请小心,但它不会t使对值的引用无效)。如果您可以用一个整数ID完全替换字符串,那么通常也会更快。
  • 避免复制地图,字符串和矢量等对象。尽可能移动它们。除了复制数据的成本外,复制容器还会导致大量相对昂贵的内存分配。

答案 1 :(得分:1)

改善两种方法:

方法1(骨架):

std:string key = "key";
auto& vec = m_unordered_map[key];
vec.reserve(n);
for (int i = 0; i != n; ++i) {
    vec.push_back(std::to_string(i));
}

方法2(骨骼)

std:string key = "key";
std::vector<std::string> vec;
vec.reserve(n);
for (int i = 0; i != n; ++i) {
    vec.push_back(std::to_string(i));
}
m_unordered_map[key] = std::move(vec));

所以

  • 只进行一次查找
  • 使用移动而不是复制。

在我这边,我将创建一种方法来构建矢量,例如:

std::vector<std::string> create_iota_strings(std::size_t n)
{
    std::vector<std::string> vec;
    vec.reserve(n);
    for (std::size_t i = 0; i != n; ++i) {
        vec.push_back(std::to_string(i));
    }
    return vec;
}

然后简单

m_unordered_map[key] = create_iota_strings(n);
如果您知道密钥不存在,那么

insert / emplace可能比operator[]更合适。 如果可能存在密钥try_emplace将是避免构造空向量的解决方案(当密钥不存在时)。