在 std::map 和 std::unordered_map 之间进行选择 [重复]
Posted
技术标签:
【中文标题】在 std::map 和 std::unordered_map 之间进行选择 [重复]【英文标题】:Choosing between std::map and std::unordered_map [duplicate] 【发布时间】:2011-04-23 14:11:32 【问题描述】:既然std
在unordered_map
中有一个真正的哈希映射,为什么(或何时)我仍想在实际存在的系统上使用旧的map
而不是unordered_map
?有没有我无法立即看到的明显情况?
【问题讨论】:
【参考方案1】:作为already mentioned,map
允许以排序方式遍历元素,但unordered_map
不允许。这在许多情况下都非常重要,例如显示集合(例如地址簿)。这也体现在其他间接方式中,例如:(1) 从 find()
返回的迭代器开始迭代,或 (2) 存在像 lower_bound()
这样的成员函数。
另外,我认为最坏情况 搜索的复杂性有所不同。
对于map
,是O(lg N)
对于unordered_map
,为 O( N ) [这可能发生在散列函数不好导致太多散列冲突时。]
这同样适用于最坏情况 删除复杂性。
【讨论】:
你对最坏情况的看法是正确的,但这篇文章在某种程度上具有误导性 - 因为平均 std::unordered_map 的搜索复杂度为 O(1),比 std::map 好得多 了解对于某些应用程序而言,最坏情况下的性能至关重要,并且是决定性因素,这一点非常重要。对于一些硬实时系统,像哈希表这样的线性最坏情况是不可接受的。 std::map 总是 O(lg N),这是一个非常好的属性。【参考方案2】:除了上面的答案,您还应该注意,仅仅因为unordered_map
是恒定速度(O(1)
)并不意味着它比map
(顺序为log(N)
)快。该常数可能大于log(N)
,尤其是因为N
受232(或264)的限制。
因此,除了其他答案(map
维护顺序和哈希函数可能很困难)之外,map
的性能可能更高。
例如,在我为blog post 运行的程序中,我发现对于VS10,std::unordered_map
比std::map
慢(尽管boost::unordered_map
比两者都快)。
注意第 3 到第 5 小节。
【讨论】:
这张图中N的值是多少? @paulm,正如我在blog postN=10,000,000
中所说的那样。
博客链接已经走上了渡渡鸟的道路,如果没有上下文,这里提供的结果几乎没有价值,因为散列和比较事物所需的时间因确切的散列函数而有很大差异,数据类型、长度和值。这对于 VC++ 标准库尤其重要,因为散列函数速度很快但容易发生冲突:通过的数字保持不变,在散列值中组合了沿任意长度的字符串间隔的 10 个字符,桶计数不是素数。 (GNU 处于光谱的另一端)。
VS10,这就是你的问题。
@n.m.遗憾的是,我的时间机器在 2010 年 10 月 11 日没有工作。【参考方案3】:
这要归功于 Google 的 Chandler Carruth 在他的CppCon 2014 lecture
std::map
(很多人认为)对于面向性能的工作没有用处:如果您想要 O(1) 分期访问,请使用适当的关联数组(或者如果缺少关联数组,std::unorderded_map
);如果您想要排序的顺序访问,请使用基于向量的东西。
另外,std::map
是一棵平衡树;你必须非常频繁地遍历它,或者重新平衡它。这些分别是缓存杀手和缓存启示录操作……所以对std::map
说不。
您可能对this SO question 感兴趣,了解高效的哈希映射实现。
(PS - std::unordered_map
对缓存不友好,因为它使用链表作为存储桶。)
【讨论】:
【参考方案4】:我认为很明显你会使用std::map
,你需要按排序顺序遍历地图中的项目。
当您更喜欢编写比较运算符(直观)而不是哈希函数(通常非常不直观)时,您也可以使用它。
【讨论】:
【参考方案5】:假设您有非常大的键,可能是大字符串。要为大字符串创建哈希值,您需要从头到尾遍历整个字符串。密钥的长度至少需要线性时间。但是,当您仅使用键的 >
运算符搜索二叉树时,每个字符串比较都可以在找到第一个不匹配时返回。对于大字符串来说,这通常很早。
这个推理可以应用于std::unordered_map
和std::map
的find
函数。如果密钥的性质是生成哈希(在std::unordered_map
的情况下)比使用二分搜索找到元素的位置(在std::map
的情况下)需要更长的时间,它应该更快地在std::map
中查找键。很容易想到会出现这种情况的场景,但我相信在实践中这种情况非常罕见。
【讨论】:
以上是关于在 std::map 和 std::unordered_map 之间进行选择 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
如何在 std::tuple 中合并 std::unordered_map?
当 std::unordered_map 包装在一个类中时,它不接受 std::thread
C++ std::map 和 std::vector 的优点? [关闭]