std :: map insert或std :: map find?

时间:2008-09-18 21:14:26

标签: c++ optimization stl stdmap

假设您要保留现有条目的地图。 20%的情况下,您插入的条目是新数据。使用返回的迭代器执行std :: map :: find然后std :: map :: insert是否有优势?或者是否更快尝试插入然后根据迭代器是否指示记录是否插入来执行操作?

9 个答案:

答案 0 :(得分:136)

答案是你不这样做。相反,您希望Effective STLScott Meyers第24项建议做一些事情:

typedef map<int, int> MapType;    // Your map type may vary, just change the typedef

MapType mymap;
// Add elements to map here
int k = 4;   // assume we're searching for keys equal to 4
int v = 0;   // assume we want the value 0 associated with the key of 4

MapType::iterator lb = mymap.lower_bound(k);

if(lb != mymap.end() && !(mymap.key_comp()(k, lb->first)))
{
    // key already exists
    // update lb->second if you care to
}
else
{
    // the key does not exist in the map
    // add it to the map
    mymap.insert(lb, MapType::value_type(k, v));    // Use lb as a hint to insert,
                                                    // so it can avoid another lookup
}

答案 1 :(得分:11)

这个问题的答案还取决于创建您在地图中存储的值类型的成本:

typedef std::map <int, int> MapOfInts;
typedef std::pair <MapOfInts::iterator, bool> IResult;

void foo (MapOfInts & m, int k, int v) {
  IResult ir = m.insert (std::make_pair (k, v));
  if (ir.second) {
    // insertion took place (ie. new entry)
  }
  else if ( replaceEntry ( ir.first->first ) ) {
    ir.second->second = v;
  }
}

对于诸如int之类的值类型,上面将比查找后跟插入(在没有编译器优化的情况下)更有效。如上所述,这是因为通过地图搜索只进行一次。

但是,对insert的调用要求您已经构造了新的“value”:

class LargeDataType { /* ... */ };
typedef std::map <int, LargeDataType> MapOfLargeDataType;
typedef std::pair <MapOfLargeDataType::iterator, bool> IResult;

void foo (MapOfLargeDataType & m, int k) {

  // This call is more expensive than a find through the map:
  LargeDataType const & v = VeryExpensiveCall ( /* ... */ );

  IResult ir = m.insert (std::make_pair (k, v));
  if (ir.second) {
    // insertion took place (ie. new entry)
  }
  else if ( replaceEntry ( ir.first->first ) ) {
    ir.second->second = v;
  }
}

为了调用'insert',我们正在为构建我们的值类型的昂贵调用付费 - 而且根据您在问题中所说的,您将不会在20%的时间内使用此新值。在上面的例子中,如果更改map值类型不是一个选项,那么首先执行'find'检查是否需要构造元素会更有效。

或者,可以更改地图的值类型,以使用您喜欢的智能指针类型存储数据的句柄。对insert的调用使用空指针(构造起来非常便宜),并且只有在必要时才构造新的数据类型。

答案 2 :(得分:8)

2之间的速度几乎没有差别,find会返回迭代器,insert会做同样的事情并且无论如何都要搜索地图以确定该条目是否已经存在。

所以..这取决于个人喜好。我总是尝试插入,然后在必要时更新,但有些人不喜欢处理返回的对。

答案 3 :(得分:5)

我认为如果你做了一个查找然后插入,额外的费用将是你找不到密钥并在之后执行插入。这有点像按字母顺序浏览书籍而不是查找书籍,然后再次浏览书籍以查看插入的位置。它归结为你将如何处理密钥以及它们是否在不断变化。现在有一些灵活性,如果你没有找到它,你可以记录,例外,做你想做的任何事情......

答案 4 :(得分:3)

我迷失在最佳答案上。

查找返回map.end()如果找不到任何意味着你要添加新内容的话

iter = map.find();
if (iter == map.end()) {
  map.insert(..) or map[key] = value
} else {
  // do nothing. You said you did not want to effect existing stuff.
}

的两倍
map.insert

对于地图中尚未存在的任何元素,因为它必须搜索两次。一旦看到它是否存在,再次找到放置新东西的地方。

答案 5 :(得分:1)

如果您担心效率,可能需要查看hash_map<>

通常映射&lt;&gt;实现为二叉树。根据您的需要,hash_map可能更有效。

答案 6 :(得分:1)

我似乎没有足够的积分来发表评论,但是勾选的答案似乎很长时间给我 - 当你认为插入返回迭代器时,为什么要搜索lower_bound,当你可以使用迭代器返回。奇怪。

答案 7 :(得分:0)

关于效率的任何答案都取决于STL的确切实施。确切知道的唯一方法是以两种方式对其进行基准测试。我猜这种差异可能不大,所以根据你喜欢的风格来决定。

答案 8 :(得分:-2)

map [key] - 让stl对它进行排序。这是最有效地传达你的意图。

是的,公平的。

如果您执行查找,然后执行插入操作,则在执行错误时执行2 x O(日志N),因为查找只会让您知道是否需要插入插入位置(lower_bound可能有帮助)你在那里)。只是直接插入,然后检查结果就是我要去的方式。