在不使用 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->operator[](key)
。
【参考方案1】:
您只需这样做(对于map
和unordered_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_type
是 pair<const int, int>
。任何开销很可能都会被优化掉。【参考方案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[]
,如果这是您唯一需要对地图做的事情。
所有insert
、emplace
和operator[]
都可以在不同情况下执行value_type
的附加构造:insert
和emplace
在插入发生之前执行此操作,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 的情况下获得“插入或更新”行为?
如何使用 Java Spring Boot 在不插入新值的情况下更新表中的现有值