std::map 或 std::unordered_map 中的哪个容器适合我的情况

which container from std::map or std::unordered_map is suitable for my case

我不知道红黑树是如何使用字符串键的。我已经在 youtube 上看到它的数字,这让我很困惑。但是我非常清楚 unoredred_map 是如何工作的(哈希映射的内部)。 std::map 对我来说仍然很深奥,但我阅读并测试了如果我们在 std::map 中没有太多变化,它可以击败哈希映射。

我的情况很简单,我有一个 std::map 的 <std::string,bool>。键包含 XML 元素的路径(键示例:"Instrument_Roots/Instrument_Root/Rating_Type"),我在 SAX 解析器中使用布尔值来了解我们是否到达了特定元素。

这张地图是我建的"only once";然后我所做的就是使用 std::find 来搜索特定的 "key" ("path") 是否存在,以便将其布尔值设置为 true,或者搜索具有 [=23 的第一个元素=] 作为关联值并使用其对应的 "key",最后我将所有布尔值设置为 false 以保证只有一个 "key" 具有 "true" 布尔值。

您无需了解 red-black 树的工作原理即可了解如何使用 std::map。它只是一个键按顺序排列的关联数组(字典顺序,在字符串键的情况下,至少使用默认比较函数)。这意味着您不仅可以在 std::map 中查找键,还可以根据顺序进行查询。例如,您可以在地图中找到不大于您拥有的键的最大键。您可以找到下一个较大的键。或者(同样在字符串的情况下)您可以找到所有以相同前缀开头的键。

如果您遍历 std::map 中的所有 key-value 对,您将按键按顺序看到它们。这有时非常有用。

额外的功能是有代价的。 std::map 通常比 std::unordered_map 慢(尽管并非总是如此;对于大字符串键,计算哈希函数的开销可能会很明显),并且底层数据结构有一定的开销,因此它们可能会占用更多space。通常的建议是使用 std::map 如果您发现这些键被命令为必不可少甚至有用的事实。

但是如果您已经对您的应用程序进行了基准测试并得出结论,std::map 也更快,那么请继续使用它:)


映射类型为 bool 的映射偶尔会有用,但前提是您需要区分对应值为 false 的键和映射中不存在的键根本。实际上,std::map<T, bool>(或std::unordered_map<T, bool>)为每个可能的键提供了一个三元选择。

如果您不需要区分这两种 false 情况,并且您不经常更改键的值,那么您最好使用 std::set(或std::unordered_set),这是完全相同的数据结构,但没有每个元素中 bool 的开销。 (尽管 bool 中只有一位有用,但对齐方面的考虑最终可能会为每个条目使用 8 个额外的字节。)除了存储 space 之外,不会有太多(如果有的话)性能差异, 不过

如果您确实需要三元案例,那么您可以 well-advised 将值设置为 enum 而不是 booltruefalse 在您的使用上下文中是什么意思?我的猜测是它们不是指 "true" 和 "false"。相反,它们的意思类似于 "is an attribute path" 和 "is an element path"。使用 enum PathType {ATTRIBUTE_PATH, ELEMENT_PATH}; 可以使这种区别更加清晰(因此 accident-prone 更少)。这不会涉及任何额外的资源,因为 bool 在任何情况下都会占用八个字节的存储空间(因为对齐)。


顺便说一下,不能保证底层数据结构恰好是一棵 red-black 树,尽管如果没有某种 self-balancing 树就很难实现性能保证。我不知道这样的实现,但是可以使用 k-ary 树(对于一些小的 k)来利用 SIMD 矢量比较操作,例如。当然,这需要针对适当的密钥类型进行自定义。

如果您确实想理解 red-black 树,您可能比 Robert Sedgewick 关于算法的标准教科书做得更糟。在本书的网站上,您会在 chapter on balanced trees.

中找到简短的插图说明

我建议你使用 std::unordered_set 因为你真的不需要存储这个布尔标志,你也不需要按排序顺序保存这些 xml 标签所以 std::unordered_set 在我看来是合乎逻辑且最有效的选择。