什么时候使用std::unordered_map::emplace_hint?

31
我知道如何使用 std::unordered_map::emplace,但是如何使用 emplace_hint 呢? cpluspluscppreference 都没有提供一组示例,说明我们如何知道元素应该放在哪里。
有人能够提供关于此的信息或者提供一些示例/说明,在什么情况下我们可以知道应该将元素放置在哪里吗?
1个回答

29
unordered_map 中的 emplace_hint 可以利用提示参数快速失败,如果迭代器所指向的元素与要插入的元素的键相同,则只需进行一次键比较即可失败 - 无需进行哈希或在桶中寻找任何散列冲突元素列表。但是,如果键不匹配,则提示参数将无效,因为任何其他键 - 无论价值如何接近 - 都应(概率上)位于完全不相关的桶中(根据通常被认为是“良好”的哈希函数),因此进行键比较只会浪费时间,并且必须重新开始,就像是一个普通的 emplace 一样。当您插入预先按键排序的元素并试图在此过程中删除大量重复项时,这可能非常有用,但由于键太大而难以保留键的副本,或者哈希函数特别缓慢。

unordered_map::emplace_hint 的另一个好处是更好地与 map::emplace_hint 的 API 兼容,因此代码可以切换容器类型,而不会破坏编译,尽管它们可能比切换到 emplace() 更慢,因为在 map 中有助于处理紧密但不同的键提示可能在 unordered_map 中无用。

仅查看 GCC 10.2 g++ -E 的输出,以查看它是否符合上述描述。 emplace_hint 调用 _M_insert_multi_node(...),其中有这一行:

__node_base* __prev = __builtin_expect(__hint != nullptr, false)
                      && this->_M_equals(__k, __code, __hint)
                      ? __hint
                      : _M_find_before_node(__bkt, __k, __code);

上述代码中,__k 代表可能被插入的键,__code 代表哈希码,__hint 代表提示迭代器/指针;_M_equals(...) 返回:

return _Equal_hash_code<__node_type>::_S_equals(__c, *__n) &&
       _M_eq()(__k, this->_M_extract()(__n->_M_v()));

因此,它将检查哈希码是否相等(如果您已经计算了哈希值,则是一次快速且不太严谨的检查),并检查键是否相等(这是针对长字符串的高质量哈希值等潜在较慢的操作)然后才使用提示迭代器。这是它使用提示的唯一情况。想象一下逻辑上有一些具有碰撞元素的桶,其键为K1、K2、K3、K4,而您的提示迭代器指向K4,但您要尝试插入一个与K2重复的元素:由于迭代器只能前进,您必须使用_M_find_before_node(...)函数来到达比您的提示指向的更早的碰撞元素。在_M_find_before_node(...)之后,您可以从K1向前扫描,以查看要插入的键-K2-是否已经出现在发生碰撞的元素中。

(如果已知键比较是便宜的,那么实现可以通过跳过哈希比较来进行改进,但是用类型特征正确得到这个条件可能会有些棘手-如何知道哪些键相等函数是便宜的?至少当使用默认的std::equals<>比较实例化无序容器时,可以假定对于小型、标准布局、可平凡复制的类型或类似类型键相等函数是便宜的...)。


6
我没有完全理解这个答案,可能是因为措辞的原因。所以除非我预订了多个密钥,否则这是无用的? - Dean
@Dean:你不一定需要“预订多个键”——可能是它们自然发生的顺序意味着重复的键有足够高的连续出现概率,使得保留迭代器到最后放置的值是值得的,因为你可以快速拒绝重复。不过,这仅基于我能想到的提示唯一可能的用途——如果你的实现实际上没有使用提示,那么提供它就是浪费时间和精力。 - Tony Delroy
1
也许有一个函数返回一个迭代器到元素(如果找到)或一个有效的提示(如果没有找到)会很有用 - 就像你可以使用 lower_bound 来搜索或生成一个 map 的提示一样。 - user253751
这个提示完全没有任何意义。根据一个良好的哈希函数,桶是绝对随机选择的,因此两个不同的键之间不会有任何对应关系。当然,对于unordered_multimap来说情况就不同了,因为多个相等的键被映射到同一个桶中。但对于unordered_map而言,emplace_hint是一个纯粹无意义的API。 - Bonita Montero
@BonitaMontero:同意,那又怎样? - Tony Delroy
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接