std::unordered_map::emplace
,但是如何使用 emplace_hint
呢? cplusplus 和 cppreference 都没有提供一组示例,说明我们如何知道元素应该放在哪里。有人能够提供关于此的信息或者提供一些示例/说明,在什么情况下我们可以知道应该将元素放置在哪里吗?
std::unordered_map::emplace
,但是如何使用 emplace_hint
呢? cplusplus 和 cppreference 都没有提供一组示例,说明我们如何知道元素应该放在哪里。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<>
比较实例化无序容器时,可以假定对于小型、标准布局、可平凡复制的类型或类似类型键相等函数是便宜的...)。
lower_bound
来搜索或生成一个map
的提示一样。 - user253751