如何优化std::map字符串键比较 避免两次完整字符串比对
问题
std::map的比较逻辑为判断待查找值是否小于当前待比较值,这意味着当map的键为字符串类型时,若两个字符串相等,会对完整字符串执行两次比较。
我曾尝试通过记录上一次参与比较的字符串,当比对相同字符串时直接复用上一次的比较结果来规避该问题。但我确认这种实现不具备线程安全性,意味着每次使用该map都需要加锁。
以下是我的测试代码:
#include <iostream> #include <chrono> #include <string> #include <map> const char* lastComparedlhs = nullptr; const char* lastComparedrhs = nullptr; int lastCompResult = 0; struct ComparerForMap { bool operator()(const std::string& lhs, const std::string& rhs) const { if (rhs.data() == lastComparedlhs && lhs.data() == lastComparedrhs) { lastComparedlhs = nullptr; lastComparedrhs = nullptr; return lastCompResult != 0; } lastComparedlhs = lhs.data(); lastComparedrhs = rhs.data(); lastCompResult = lhs.compare(rhs); return lastCompResult < 0; } }; int main() { std::map<std::string, int> normalMap; std::map<std::string, int, ComparerForMap> specialMap; std::string str1(10000000, 'a'); std::string str2(str1); normalMap[str1] = 123; specialMap[str1] = 123; auto start1 = std::chrono::high_resolution_clock::now(); int n1 = normalMap[str2]; auto stop1 = std::chrono::high_resolution_clock::now(); auto duration1 = std::chrono::duration_cast<std::chrono::nanoseconds>(stop1 - start1); auto start2 = std::chrono::high_resolution_clock::now(); int n2 = specialMap[str2]; auto stop2 = std::chrono::high_resolution_clock::now(); auto duration2 = std::chrono::duration_cast<std::chrono::nanoseconds>(stop2 - start2); std::cout << "normalMap: " << duration1.count() << '\n'; std::cout << "specialMap: " << duration2.count() << "\npress enter to exit\n"; // normalMap: about 4000000 ns // specialMap: about 2000000 ns char ch = getchar(); }
请问是否存在更优的实现方式?
回答
你写的缓存上次比较结果的方案存在本质缺陷:除了线程不安全之外,它强依赖std::map内部对比较器的调用顺序,而C++标准从未对关联容器比较器的调用顺序、调用次数做强制约束,一旦更换标准库版本、调整编译优化选项,哪怕是单线程场景也可能触发未定义行为。
可根据实际业务场景选择以下更可靠的优化方案:
- 不需要对键做有序遍历时,直接替换为
std::unordered_map。哈希表的查找逻辑仅需计算一次键哈希、做一次相等性校验,完全不存在两次全量字符串比较的问题,性能比你实现的hack比较器更高,且符合容器常规线程安全规则,不需要额外加锁。 - 必须使用有序
std::map的场景,自定义带预校验信息的键类型:
封装专属的字符串键结构,内部除了存储原始字符串,提前预计算并存储字符串长度、字符串哈希值。比较器按优先级执行判断:- 先比较两个键的长度,长度不一致直接返回大小结果
- 长度一致再比较预存的哈希值,哈希不一致直接返回大小结果
- 只有长度、哈希完全相等时,才执行实际的字符串逐字符比较
这种实现的比较器是完全无状态的,不存在全局共享变量,天然线程安全,能覆盖绝大多数长字符串比较的优化场景。
- 若使用C++20及以上版本,可利用三向比较特性优化。部分新版本标准库实现支持为
std::map透传三向比较结果,避免等价判断时的第二次比较调用,使用前需要确认你所用的标准库版本是否支持该特性。
注意:永远不要在比较器内部用全局变量、静态变量缓存比较状态,这类基于库实现细节的hack写法可移植性极差,在不同环境下随时可能失效。
内容的提问来源于stack exchange,提问作者my_stack_exchange_account
相关产品推荐
相关产品推荐

