C++ STL unordered_map中count()为何比[]运算符访问耗时更低
问题背景
刷LeetCode题目时观测到性能差异现象,对应题目为Naming a Company。
初始解题代码如下,提交后触发TLE(超时错误),仅通过85/89个测试用例:
long long distinctNames(vector<string>& ideas) { unordered_map<string,bool> isPresent; vector<vector<long long>> dp(26,vector<long long>(26,0)); int n = ideas.size(); long long ans = 0; for(int i = 0; i < n; i++) isPresent[ideas[i]] = true; for(int i = 0; i < n; i++) { char x = ideas[i][0]; string ts = ideas[i]; for(int j = 0; j < 26; j++) { char y = 'a' + j; ts[0] = y; if(!isPresent[ts]) dp[x-'a'][j]++; } } for(int i = 0; i < 26; i++) { for(int j = 0; j < 26; j++) { if(i==j) continue; ans += (dp[i][j] * dp[j][i]); } } return ans; }
其余代码完全不变的前提下,仅将判断逻辑!isPresent[ts]替换为!isPresent.count(ts),代码运行速度即大幅提升,顺利通过所有测试用例。
性能差异根本原因
两者性能差距来自unordered_map两个接口的语义和执行逻辑本质区别:
operator[]不是只读查询接口,是带插入副作用的修改接口
当调用isPresent[ts]时,如果ts这个key不在哈希表中,该操作会自动向哈希表插入一个新键值对:key为ts,value为bool类型默认值false,之后返回这个value的引用。
你的代码逻辑中,每个原始字符串会遍历替换26次首字母生成ts,其中绝大多数ts都不在初始的isPresent里,相当于每次判断都在往哈希表中插入大量无效键值对:- 插入操作本身需要做字符串哈希计算、内存分配、哈希冲突处理,开销远高于单纯的查找
- 随着无效元素越插越多,哈希表负载因子持续升高,会频繁触发rehash(重建哈希表)操作,后续所有哈希操作的耗时会快速上涨
- 大量无效字符串key也会带来额外的内存占用,进一步拖慢运行效率
count()是纯只读查询接口
该方法只会检查key是否存在于哈希表中,存在返回1、不存在返回0,全程不会修改哈希表的结构、大小、负载因子,没有任何额外的插入、内存分配、rehash开销,仅需要承担哈希查找的固定成本,在你这个绝大多数查询key不存在的场景下,性能远高于误用的operator[]。
最佳实践提示:对于所有C++关联容器(
unordered_map/map/unordered_set/set),如果仅需要判断key是否存在,优先使用count()或find()方法,不要在只读场景调用operator[],除非你明确需要“key不存在就插入默认值”的语义。
内容的提问来源于stack exchange,提问作者Digvijaysingh Gour
相关产品推荐
相关产品推荐

