STL列表 - 如何通过其对象字段查找列表元素

时间:2010-05-14 01:30:04

标签: c++ list stl

我有一个清单:

list<Unit *> UnitCollection;

包含Unit对象,其中包含一个访问者:

bool Unit::isUnit(string uCode)
{
    if(this->unitCode == uCode)
        return true;
    else
        return false;
}

如何通过uCode搜索我的UnitCollection列表并返回相应的元素(最好是迭代器)。

在伪代码中,它看起来像这样:

for every item in my UnitCollection:
  if the unit.isUnit(someUnitIpass)
    do something
  else
    next unit

我查看了find()方法,但是我不确定你是否可以传入一个布尔方法,而不是搜索项参数,如果这是有意义的。

4 个答案:

答案 0 :(得分:4)

首先是一个无关紧要的评论

您可以将访问者功能更改为更简单的格式

return unitCode == uCode;

现在我们在这里

你最好寻找元素的位置而不是它的索引。从索引获取元素是O(n)操作,而从其位置获取元素是O(1)操作。所以,使用STL和boost::bind()的一点帮助:

#include <algorithm>
#include <boost/bind.hpp>

// ...
std::string uCode("uCode to search for");
std::list<Unit*>::iterator pos = std::find_if(unitCollection.begin(),
                                              unitCollection.end(),
                                              boost::bind(&Unit::isUnit,
                                                          _1, uCode));

STL确实有std::mem_fun(),与std::bind2nd()一起会得到相同的结果。问题是mem_fun()仅适用于不带参数的成员函数。另一方面,boost::bind()功能更强大,并且可以很好地解决问题。你应该期待下一个标准,它应该在弥赛亚到来之后立即出现。

但如果你没有提升

如果项目中没有提升,那么你真的应该安装它。如果标准库是C ++的妻子,那么Boost就是C ++的年轻情人。他们都应该在那里,他们相处得很好。

话虽如此,你可以将函数提取到一个独立的函数对象中,正如彼已提到的那样:

struct has_uCode {
    has_uCode(cont std::string& uc) : uc(uc) { }
    bool operator()(Unit* u) const { return u->isUnit(uc); }
private:
    std::string uc;
};

然后,您可以这样调用std::find_if()

std::list<Unit*>::iterator pos = std::find_if(unitCollection.begin(),
                                              unitCollection.end(),
                                              has_uCode("this and that"));

还有一点性能考虑

还有一件事:我不知道uCode的样子,但如果它们很大,那么你可以通过维护这些字符串的哈希来加快速度,这样在你的搜索谓词中你只能比较哈希值。散列可能是常规整数:比较整数非常快。

还有一件事:如果您经常运行此搜索过程,您可能还会考虑更改容器类型,因为这确实是一个昂贵的过程:按列表长度的顺序。

答案 1 :(得分:3)

您可以查看jpalecek建议的find_if,然后使用distance查找从find_if和UnitCollection.begin()返回的迭代器之间的距离,该距离应该是索引列表中的元素。

至于谓词,你可以编写一个这样的函数对象:

struct predicate
{
    predicate( const std::string &uCode ) : uCode_(uCode) {}

    bool operator() ( Unit *u )
    {
        return u->isUnit( uCode_ )
    }
private:
    std::string uCode_;
};

然后像这样使用它:

predicate pred("uCode");
std::list<Unit*>::iterator i;
i = std::find_if( UnitCollection.begin(), UnitCollection.end(), pred );

或者至少我认为这是一种方法。

答案 2 :(得分:3)

您的谓词可能类似于:

struct unit_predicate {
    unit_predicate(const string& s): str(s) {}
    bool operator()(const Unit* unit) const {
        return unit->isUnit(str);
    }
    const string& str;
};

UnitCollection::const_iterator unit = std::find_if(units.begin(), units.end(), unit_predicate("Some Unit"));

其他几条评论:

你的isUnit函数最好采用(const)引用的字符串来避免不必要的副本。

你说你想要退回一个项目的索引;这通常不适用于链表,因为您无法通过索引获取项目。如果你想通过索引来处理它们,那么std::vector可能对你更有用。

答案 3 :(得分:1)

查看find_if

如果您可以使用提升,则可以将其与boost::lambda一起使用。

namespace bl=boost::lambda;
std::find_if(...begin... , ...end... , bl::bind(&Unit::isUnit, *bl::_1, "code"))

或者你可以酿造自己的仿函数。

struct isUnitor // : public std::unary_function<Unit*, bool> -- this is only needed for the negation below
{
  string arg;
  isUnitor(const string& s) : arg(s) {}
  bool operator()(Unit* u) const { return u->isUnit(arg); }
};

std::find_if(...begin... , ...end... , isUnitor("code"))

或者,如果您想要索引(对于否定,请查看here):

std::count_if(...begin... , ...end... , not1(isUnitor("code")))