在 unordered_map 中查找的性能

发布于 2024-11-28 18:00:42 字数 642 浏览 0 评论 0原文

我知道这可能是一个愚蠢的问题,但我想确定一下,但我无法轻易找到这些信息。

find() 在无序映射中的性能特征是什么?它与普通查找一样快/几乎一样快吗?

std::string defaultrow = sprite.attribute("defaultrow").value();
auto rclassIt = Rows::NameRowMap.find(defaultrow);
if (rclassIt != Rows::NameRowMap.end())
    defRow = rclassIt->second;

vs.

std::string defaultrow = sprite.attribute("defaultrow").value();
defRow = Rows::NameRowMap[defaultrow];

其中 Rows::NameRowMap 是将字符串索引映射到 int 的无序映射。

就我而言,我不确定密钥是否会事先存在,因此第一个解决方案对我来说似乎更安全,但如果我可以保证存在,那么第二种情况会更快(忽略我正在做的额外检查) ?如果是这样,为什么?如果重要的话,我正在使用 1.46 boost 实现

I know this is probably a stupid question, but I wanted to make sure and I couldn't readily find this information.

What is the performance characteristic of find() in an unordered map? Is it as fast/nearly as fast as a normal lookup?

I.e.

std::string defaultrow = sprite.attribute("defaultrow").value();
auto rclassIt = Rows::NameRowMap.find(defaultrow);
if (rclassIt != Rows::NameRowMap.end())
    defRow = rclassIt->second;

vs.

std::string defaultrow = sprite.attribute("defaultrow").value();
defRow = Rows::NameRowMap[defaultrow];

where Rows::NameRowMap is a unordered map mapping a string index to an int.

In my case, I don't know for certain if the key will exist before hand, so the first solution seemed safer to me, but if I can guarantee existence, is the second case faster (ignoring the extra checks I'm doing)? and if so, why? If it matters, I'm using the 1.46 boost implementation

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

念三年u 2024-12-05 18:00:42

无序容器上的 findoperator[] 平均为 O(1),最坏情况为 O(n) - 这取决于哈希函数的质量。

find and operator[] on an unordered container are O(1) average, O(n) worst-case -- it depends on the quality of your hash function.

风铃鹿 2024-12-05 18:00:42

operator[] 很可能在内部使用 findinsert 。例如,IIRC Miscrosoft 的 std::map 实现就是这种情况。

编辑:
我想说的是operator[]并不神奇,它仍然需要先进行查找。从我在 Boost 1.46.0 中看到的情况来看,find 和所说的运算符都在内部使用 find_iterator

通常最好使用 find 进行查找,因为您的代码将更加可重用和健壮(例如,您永远不会意外插入某些内容),尤其是在某种通用代码中。

It's pretty possible that operator[] uses find and insert internally. For example, IIRC that's the case with Miscrosoft's std::map implementation.

EDIT:
What I was trying to say is that operator[] is not magical, it still has to do a lookup first. From what I see in Boost 1.46.0 both find and said operator use find_iterator internally.

Usually it's better to use find for lookups, because your code will be more reusable and robust (e.g. you will never insert something accidentally), especially in some kind of generic code.

可可 2024-12-05 18:00:42

它们具有相同的摊余复杂度 O(1),但当未找到值时,运算符也会创建一个新元素。如果找到该值,则性能差异应该很小。我的 boost 有点旧 - 版本 1.41,但希望没关系。这是代码:

// find
//
// strong exception safety, no side effects
template <class H, class P, class A, class G, class K>
BOOST_DEDUCED_TYPENAME hash_table<H, P, A, G, K>::iterator_base
hash_table<H, P, A, G, K>::find(key_type const& k) const
{
    if(!this->size_) return this->end();

    bucket_ptr bucket = this->get_bucket(this->bucket_index(k));
    node_ptr it = find_iterator(bucket, k);

    if (BOOST_UNORDERED_BORLAND_BOOL(it))
        return iterator_base(bucket, it);
    else
        return this->end();
}

// if hash function throws, basic exception safety
// strong otherwise
template <class H, class P, class A, class K>
    BOOST_DEDUCED_TYPENAME hash_unique_table<H, P, A, K>::value_type&
hash_unique_table<H, P, A, K>::operator[](key_type const& k)
{
    typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type;

    std::size_t hash_value = this->hash_function()(k);
    bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);

    if(!this->buckets_) {
        node_constructor a(*this);
        a.construct_pair(k, (mapped_type*) 0);
        return *this->emplace_empty_impl_with_node(a, 1);
    }

    node_ptr pos = this->find_iterator(bucket, k);

    if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
        return node::get_value(pos);
    }
    else {
        // Side effects only in this block.

        // Create the node before rehashing in case it throws an
        // exception (need strong safety in such a case).
        node_constructor a(*this);
        a.construct_pair(k, (mapped_type*) 0);

        // reserve has basic exception safety if the hash function
        // throws, strong otherwise.
        if(this->reserve_for_insert(this->size_ + 1))
            bucket = this->bucket_ptr_from_hash(hash_value);

        // Nothing after this point can throw.

        return node::get_value(add_node(a, bucket));
    }
}

They have the same amortized complexity of O(1), but the operator also creates a new element when the value is not found. If the value is found, performance difference should be minor. My boost is a little old - version 1.41, but hopefully it does not matter. Here is the code:

// find
//
// strong exception safety, no side effects
template <class H, class P, class A, class G, class K>
BOOST_DEDUCED_TYPENAME hash_table<H, P, A, G, K>::iterator_base
hash_table<H, P, A, G, K>::find(key_type const& k) const
{
    if(!this->size_) return this->end();

    bucket_ptr bucket = this->get_bucket(this->bucket_index(k));
    node_ptr it = find_iterator(bucket, k);

    if (BOOST_UNORDERED_BORLAND_BOOL(it))
        return iterator_base(bucket, it);
    else
        return this->end();
}

// if hash function throws, basic exception safety
// strong otherwise
template <class H, class P, class A, class K>
    BOOST_DEDUCED_TYPENAME hash_unique_table<H, P, A, K>::value_type&
hash_unique_table<H, P, A, K>::operator[](key_type const& k)
{
    typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type;

    std::size_t hash_value = this->hash_function()(k);
    bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);

    if(!this->buckets_) {
        node_constructor a(*this);
        a.construct_pair(k, (mapped_type*) 0);
        return *this->emplace_empty_impl_with_node(a, 1);
    }

    node_ptr pos = this->find_iterator(bucket, k);

    if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
        return node::get_value(pos);
    }
    else {
        // Side effects only in this block.

        // Create the node before rehashing in case it throws an
        // exception (need strong safety in such a case).
        node_constructor a(*this);
        a.construct_pair(k, (mapped_type*) 0);

        // reserve has basic exception safety if the hash function
        // throws, strong otherwise.
        if(this->reserve_for_insert(this->size_ + 1))
            bucket = this->bucket_ptr_from_hash(hash_value);

        // Nothing after this point can throw.

        return node::get_value(add_node(a, bucket));
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文