在不使用 if 的情况下插入/更新 std::unordered_map 元素的最快方法是啥?

Posted

技术标签:

【中文标题】在不使用 if 的情况下插入/更新 std::unordered_map 元素的最快方法是啥?【英文标题】:What is the quickest way of inserting/updating std::unordered_map elements without using an if?在不使用 if 的情况下插入/更新 std::unordered_map 元素的最快方法是什么? 【发布时间】:2013-10-05 12:27:00 【问题描述】:

我目前有很多如下代码:

std::unordered_map<int,int> my_dict;
.
.
.
// If the key does exist in the dictionary
if(my_dict.count(key) == 1)
    my_dict[key] = value;


// If its a new key
else
    my_dict.insert(std::make_pair(key,value));

有什么方法可以通过每次覆盖值来加快速度?

【问题讨论】:

看来你应该使用地图 @billz 我想要 O(1) 插入时间?我不想要一棵树 O log(N) [] 执行此操作。 cplusplus.com/reference/unordered_map/unordered_map/operator[] 当然,复杂度必须是线性的。如果您想要更少的插入时间,请使用地图 my_dict[key] = value; 是你所需要的,如果需要,不需要。 @user997112 然后使用(*my_dict_ptr)[key]my_dict_ptr-&gt;operator[](key) 【参考方案1】:

您只需这样做(对于mapunordered_map

mydict[key]=value;

【讨论】:

当且仅当value 的默认构造非常昂贵时,使用if-else 会更快。不过,在大多数情况下,operator[] 都能胜任。【参考方案2】:

我认为这样可能是最快的:

auto it = my_dict.find(key);
if( it != my_dict.end() ) 
    it->second = value;

else 
    my_dict.insert(std::make_pair(key,value));

如果key 已经存在并且您只有一次查找,您就不会修改unordered_map 的结构。


如果您之后不需要/访问value,另一种选择:

my_dict[key] = std::move(value);

如果value 的分配代价高昂并且受益于移动语义,这可能会更好。

【讨论】:

@us2012 大多数情况下,但在插入情况下,它首先插入一个默认构造值,然后分配它。以上避免了这种开销。 在我走这条路之前,我会对简单案例 (foo[key]=value;) 进行一些认真的测量。 @Joe 是的。如果没有显示这是一个问题的配置文件运行,请使用更具可读性的代码。当这个问题缺少“性能”标签时,那是我的答案:) 我的一般经验是,大多数人误解了哪些事情会拖累他们的表现。我希望 OP 的代码和这里的任一解决方案之间的性能差异在unordered_map...中非常小... @DanielFrey:value_typepair&lt;const int, int&gt;。任何开销很可能都会被优化掉。【参考方案3】:

要更新 C++17,您可以使用:

std::unordered_map::insert_or_assign()

http://en.cppreference.com/w/cpp/container/unordered_map/insert_or_assign

【讨论】:

insert_or_assign() 在使用 gcc 时可能会变慢。见gcc.gnu.org/bugzilla/show_bug.cgi?id=95079【参考方案4】:

通常,您可以通过定义一个函数来避免额外的输入,从而避免您再次输入相同的内容。 如果你无法访问 C++17 的 insert_or_assign(),你可以自己实现这样的东西:

bool InsertOrAssign(std::unordered_map& m, int key, int value) 
  // Your code or one of the suggested answers goes here

【讨论】:

【参考方案5】:

您的所有地图函数都会执行搜索,因此无论键是否存在,您总是会搜索两次地图。您可以利用 insert 检索插入是否发生(键不存在)或不(键存在)的事实并采取相应措施:

std::unordered_map<int,int> mydict;
bool inserted = false;
auto position = mydict.end();
std::tie(position, inserted) = mydict.insert(key, value);
if (inserted) 
  pos->second = value;

这相当于mydict[key] = value,因为无论如何我们都在分配新值。对于默认构造便宜的类型,我会使用operator[],如果这是您唯一需要对地图做的事情。

所有insertemplaceoperator[]都可以在不同情况下执行value_type的附加构造:insertemplace在插入发生之前执行此操作,operator[]默认构造映射键不存在时的值。因此,它们不适用于构造/复制/移动成本高昂的类型(std::thread,非常大的std::array...)。在这种情况下,使用try_emplace 来代替(C++17)更合适:

std::unordered_map<int, expensive_type> mydict;
bool inserted = false;
auto position = mydict.end();
std::tie(position, inserted) = mydict.try_emplace(key, expensive_constructor_args);
if (inserted) 
  // no expensive_type has been constructed
  // pos->second references the existing value

【讨论】:

以上是关于在不使用 if 的情况下插入/更新 std::unordered_map 元素的最快方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 CONFLICT_REPLACE 的情况下获得“插入或更新”行为?

在不使用 SQL 游标的情况下插入行和更新表

如何使用 Java Spring Boot 在不插入新值的情况下更新表中的现有值

在不使用 jquery/ajax 的情况下自动将新记录插入到表中时,使用新记录更新页面的另一种方法是啥

如何在不插入新对象的情况下从数据库更新对象?

如何在不使用 jquery append 的情况下插入元标记?