【问题标题】:C++ inserting in map() is taking a long time在 map() 中插入 C++ 需要很长时间
【发布时间】:2019-11-20 02:51:43
【问题描述】:

我有一个map 类型为<int, foo*>,我是从数据库中填充的。 int 是一个唯一键,所以在填充之前,我会检查键是否已经在映射中,如果不是 foo 类型的新对象被创建并插入到映射中。

foo 类还有另一个映射作为属性,类型为<int, float>,也需要从数据库中填充。我有下面的代码,据说很简单:

std::map<int, foo> MAP;

while(getline(infile, line))
{ //reading records in data file
    string item;
    stringstream ss(line);
    vector<string> splittedString;

    int a = stoi(splittedString[0]); // cells after splitting data
    int b = stoi(splittedString[1]);
    float c = stof(splittedString[2]);


    if (MAP.find(a) == MAP.end())
    {
        foo f = foo();
        MAP.insert({a, &f});
        f.fooMap.insert({b, c/100});
    }
    else
    {
        foo* f = MAP.at(a);
        f->fooMap.insert({b, c/100});
    }
}

计算需要花费大量时间。大约有 50,000 条记录,但这不应该永远持续下去。我很确定我做错了什么,因为我对 c++ 有点生疏了。

我知道在创建 foo 对象时我有一些冗余,我认为执行上述操作的更有效方法是下面的“不正确”代码:

while(getline(infile, line))
{//reading records in data file
    string item;
    stringstream ss(line);
    vector<string> splittedString;

    int a = stoi(splittedString[0]); //cells after splitting data
    int b = stoi(splittedString[1]);
    float c = stof(splittedString[2]);

    foo f = *MAP[a];        
    if (f == NULL)
    {
        f = foo();
        MAP.insert({a, &f});

    }    
    f->fooMap.insert({b, c/100});
}

更新

如果我使用MAP&lt;int, foo&gt; 而不是MAP&lt;int, foo*&gt;(并相应地安排插入代码),则代码可以正常工作。所以我用指针做错了。解决这个问题的最佳方法是什么?

【问题讨论】:

  • 你知道MAP({a, &amp;f}); 是做什么的吗?为什么? MAP 是什么?
  • 个人资料?拆分和计算 c 的成本是多少?您可以只使用 insert() 该值,它会返回一个布尔值,告诉您它是否已经存在。您也可以使用 emplace() 在插入之前不构造值,但这在 c++11 中很难有效使用。在 c++17 中使用 try_emplace(),但是如果你想节省计算 c 和 foo() 的工作量,你需要发明一个可以分别由 splittedString[2] 或 foo() 构造的包装类。跨度>
  • 不要使用&lt;int, foo*&gt;。而是使用&lt;int, std::unique_ptr&lt;foo&gt;&gt;
  • @user7331538,我怀疑MAP({a, &amp;f});MAP(a) 是有效的语法。
  • 请再次查看std::map界面。没有为 std::map 定义呼叫运算符 (operator())。因此,您的 MAP(a) 要么是错误的,要么它没有按照您的预期执行...

标签: c++ dictionary object pointers insert


【解决方案1】:

您的代码比它需要的要复杂一些。您应该让std::map 完成这项工作,而不是自己尝试。你想要的是容器的.emplace 函数。它不仅在不需要时跳过插入,而且还为您提供了正确的修改条目。例如,您可以执行以下操作:

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

int main()
{
    //helper
    auto print = [](auto MAP) 
    {
        std::cout << "MAP:\n";
        for (auto && each : MAP)
            std::cout << each.first << "->" << each.second << '\n';
    };
    //work
    std::map<int, std::string> MAP;

    MAP.emplace(1,"").first->second = "one";
    MAP.emplace(2,"").first->second = "two";
    print(MAP);
    MAP.emplace(1,"").first->second = "three";
    print(MAP);
}

产生以下输出:

MAP:
1->one
2->two
MAP:
1->three
2->two

当然,这有这种奇怪的.first-&gt;second 语法,但我们可以将它重构为一个新的 lambda:

auto update = [](auto& MAP, auto index, auto entry) 
{
    MAP.emplace(index,"").first->second = entry;
};

它可以让您编写以下内容:

update(MAP, 1, "one");
update(MAP, 2, "two");
print(MAP);
update(MAP, 1, "three");
print(MAP);

编辑

因为您要求std::unique_ptr 解决方案,所以这里是:

#include <iostream>
#include <map>
#include <string>
#include <memory>

int main()
{
    //helper
    auto print = [](const auto& MAP) 
    {
        std::cout << "MAP:\n";
        for (auto && each : MAP)
            std::cout << each.first << "->" << *each.second.get() << '\n';
    };
    //work
    std::map<int, std::unique_ptr<std::string>> MAP;

    auto update = [](auto& MAP, auto index, auto entry) 
    {
        *MAP.emplace(index, std::make_unique<std::string>()).first->second.get() = entry;
    };
    update(MAP, 1, "one");
    update(MAP, 2, "two");
    print(MAP);
    update(MAP, 1, "three");
    print(MAP);
}

不幸的是,由于std::unique_ptr 不能被复制,它变得有点难以阅读并且也很烦人。 (这只是它的实现方式。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-24
    • 2016-08-13
    • 2017-12-05
    • 2014-02-22
    • 2012-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多