【问题标题】:Checking for existence in std::map - count vs find检查 std::map 中是否存在 - 计数与查找
【发布时间】:2014-10-18 20:47:09
【问题描述】:

因此,似乎有两种普遍可接受的方法来确定一个键是否存在于 std::map 中:

map.find(key) != map.end()
map.count(key) > 0

一种比另一种更有效吗?具体来说,count() 的概念可以解释为意味着该方法将遍历每个键,计算总计数(并且由于 std::map 的定义,总计数将始终为 0 或 1)。 count() 是否保证在匹配后“停止”,以与 find() 相同的复杂度运行?

【问题讨论】:

  • count 只能是 01 用于 std::map。复杂度是一样的。请注意,您不需要 > 0 比较。
  • 返回的count只能是0或者1(对于std::map),但这不等于说底层实现会保证高效完成
  • 对数复杂度是实现高效的一个很好的指标。

标签: c++ map stl stdmap


【解决方案1】:

由于地图最多只能有一个键,count 将在找到一个元素后基本上停止。但是,考虑到更通用的容器,例如多图和多集,如果您只关心具有此键的 some 元素是否存在,find 会更好,因为一旦第一个匹配元素有找到了。

一般来说,countfind 都将使用容器特定的查找方法(树遍历或哈希表查找),这些方法总是相当有效的。只是count 必须继续迭代直到相等范围的末尾,而find 不需要。此外,您的代码应该记录意图,因此如果您想查找某些内容,请使用 find

【讨论】:

  • 谢谢你的回答,虽然我想清楚你的第一句话。是计数应该在找到一个元素后停止,还是计数在找到一个元素后停止?换句话说,它对 std::map 有保证吗?
  • @dolphy:正如我所说,地图使用其内部数据结构进行查找,因此对于给定的键,它将有效地找到具有该键的所有元素的范围(例如,在一棵树),对于count,它将返回该范围的长度。
  • 现在,在 C++20 中,您可以使用 std::map::contains 检查地图中是否存在键。
【解决方案2】:

根据源码,我建议使用find。查看源代码。

在 GCC 中,代码如下 (stl_map.h):

    const_iterator
    find(const key_type& __x) const
    { return _M_t.find(__x); }

    size_type                                                                              
    count(const key_type& __x) const                                                       
    { return _M_t.find(__x) == _M_t.end() ? 0 : 1; }  

在windows平台的Visual Studio中,代码如下(xtree):

    const_iterator find(const key_type& _Keyval) const
    {   // find an element in nonmutable sequence that matches _Keyval
        const_iterator _Where = lower_bound(_Keyval);
        return (_Where == end()
            || _DEBUG_LT_PRED(this->_Getcomp(),
                _Keyval, this->_Key(_Where._Mynode()))
                    ? end() : _Where);
    }

    //....

    const_iterator lower_bound(const key_type& _Keyval) const
    {   // find leftmost node not less than _Keyval in nonmutable tree
        return (const_iterator(_Lbound(_Keyval), this));
    }

    //....

    _Nodeptr _Lbound(const key_type& _Keyval) const
    {   // find leftmost node not less than _Keyval
        _Nodeptr _Pnode = _Root();
        _Nodeptr _Wherenode = this->_Myhead;    // end() if search fails

        while (!this->_Isnil(_Pnode))
            if (_DEBUG_LT_PRED(this->_Getcomp(), this->_Key(_Pnode), _Keyval))
                _Pnode = this->_Right(_Pnode);  // descend right subtree
            else
                {   // _Pnode not less than _Keyval, remember it
                _Wherenode = _Pnode;
                _Pnode = this->_Left(_Pnode);   // descend left subtree
                }

        return (_Wherenode);    // return best remembered candidate
    }

    //..........................................
    //..........................................

    size_type count(const key_type& _Keyval) const
    {   // count all elements that match _Keyval
        _Paircc _Ans = equal_range(_Keyval);
        size_type _Num = 0;
        _Distance(_Ans.first, _Ans.second, _Num);
        return (_Num);
    }

    //....

    _Pairii equal_range(const key_type& _Keyval) const
    {   // find range equivalent to _Keyval in nonmutable tree
        return (_Eqrange(_Keyval));
    }

    //....

    _Paircc _Eqrange(const key_type& _Keyval) const
    {   // find leftmost node not less than _Keyval
        _Nodeptr _Pnode = _Root();
        _Nodeptr _Lonode = this->_Myhead;   // end() if search fails
        _Nodeptr _Hinode = this->_Myhead;   // end() if search fails

        while (!this->_Isnil(_Pnode))
            if (_DEBUG_LT_PRED(this->_Getcomp(), this->_Key(_Pnode), _Keyval))
                _Pnode = this->_Right(_Pnode);  // descend right subtree
            else
                {   // _Pnode not less than _Keyval, remember it
                if (this->_Isnil(_Hinode)
                        && _DEBUG_LT_PRED(this->_Getcomp(), _Keyval,
                        this->_Key(_Pnode)))
                    _Hinode = _Pnode;   // _Pnode greater, remember it
                _Lonode = _Pnode;
                _Pnode = this->_Left(_Pnode);   // descend left subtree
                }

        _Pnode = this->_Isnil(_Hinode) ? _Root()
            : this->_Left(_Hinode); // continue scan for upper bound
        while (!this->_Isnil(_Pnode))
            if (_DEBUG_LT_PRED(this->_Getcomp(), _Keyval, this->_Key(_Pnode)))
                {   // _Pnode greater than _Keyval, remember it
                _Hinode = _Pnode;
                _Pnode = this->_Left(_Pnode);   // descend left subtree
                }
            else
                _Pnode = this->_Right(_Pnode);  // descend right subtree

        const_iterator _First = const_iterator(_Lonode, this);
        const_iterator _Last = const_iterator(_Hinode, this);
        return (_Paircc(_First, _Last));
    }

【讨论】:

  • 使用 find 的参数是什么? GCC 示例显示 count 是根据 find 实现的,那么在这种情况下我为什么更喜欢 find 呢?
【解决方案3】:

如果你只是想知道键是否存在,而不关心值,最好使用map::count,因为它只返回一个整数。 map::find 返回一个迭代器,因此通过使用count,您将节省迭代器的构造。

【讨论】:

  • 正如您在源代码中看到的(另一个答案) - 与“find”的实现相比,“count”的实现在内部构造了更多的迭代器。
  • @Ezh 在那个答案中(我猜你的意思是 Bright Chen 的),对于 GCC,使用的迭代器是相同的,而不是更多。根据代码,if(m.count(i))if(m.find(i) != m.end()) 的作用相同。
猜你喜欢
  • 1970-01-01
  • 2018-12-20
  • 2010-12-28
  • 2023-01-12
  • 2010-10-06
  • 2010-12-21
  • 2020-08-18
  • 1970-01-01
相关资源
最近更新 更多