在 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 【问题描述】:

既然stdunordered_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_mapstd::map 慢(尽管boost::unordered_map 比两者都快)。

注意第 3 到第 5 小节。

【讨论】:

这张图中N的值是多少? @paulm,正如我在blog post N=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_mapstd::mapfind函数。如果密钥的性质是生成哈希(在std::unordered_map 的情况下)比使用二分搜索找到元素的位置(在std::map 的情况下)需要更长的时间,它应该更快地在std::map 中查找键。很容易想到会出现这种情况的场景,但我相信在实践中这种情况非常罕见。

【讨论】:

以上是关于在 std::map 和 std::unordered_map 之间进行选择 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何在 std::tuple 中合并 std::unordered_map?

std::map的insert和下标[]操作区别

当 std::unordered_map 包装在一个类中时,它不接受 std::thread

C++ std::map 和 std::vector 的优点? [关闭]

从嵌套在 std::map 中的 std::list 中删除元素的最佳方法

std::map 和性能,相交集