std :: map插入还是std :: map查找?


90

假设要在其中保留现有条目的地图。20%的时间,您要插入的条目是新数据。使用返回的迭代器执行std :: map :: find然后进行std :: map :: insert有好处吗?还是尝试插入然后根据迭代器是否指示记录已插入而采取行动来更快?


4
我已得到纠正,并打算使用std :: map :: lower_bound代替std :: map :: find。
Superpolock

Answers:


147

答案是你什么都不做。相反,您想执行Scott Meyers的《有效STL的第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
}

2
这确实是find的工作方式,诀窍在于,它结合了find和insert所需的搜索。当然,只使用插入然后查看第二个返回值也是如此。
puetzk

1
两个问题:1)使用lower_bound与为地图使用find有何不同?2)对于“地图”,当“ lb!= mymap.end()”时,&&的右手操作是否始终为真?
理查德·科登

11
@Richard:如果键不存在,则find()返回end(),lower_bound返回该项目应位于的位置(又可用作插入提示)。@puetzek:是否“仅插入”会覆盖现有键的引用值?不确定OP是否希望这样做。
peterchen

2
有谁知道unordered_map是否有类似的东西?
乔瓦尼·丰沙尔

3
@peterchen map :: insert不会覆盖现有值(如果存在),请参见cplusplus.com/reference/map/map/insert
克里斯·德鲁

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之类的值类型,上述方法将比在插入后进行查找(在没有编译器优化的情况下)更有效。如上所述,这是因为通过地图的搜索仅进行一次。

但是,插入调用要求您已经构造了新的“值”:

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;
  }
}

为了调用“插入”,我们为构建值类型的昂贵调用付出了代价-根据您在问题中所说的,您不会在20%的时间内使用此新值。在上述情况下,如果不选择更改地图值类型,则首先执行“查找”以检查是否需要构造元素会更有效。

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


8

2之间的速度几乎没有任何区别,find将返回一个迭代器,insert进行相同操作,并且无论如何都会搜索该映射以确定该条目是否已经存在。

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


5

我认为如果您先查找然后插入,那么额外的费用将是您找不到密钥并在之后执行插入操作。这有点像按字母顺序浏览书籍,而不是查找书籍,然后再次浏览书籍以查看将其插入到哪里。归结为您将如何处理按键以及它们是否不断变化。现在有了一些灵活性,如果您找不到它,则可以记录日志,进行异常,执行任何您想做的事情...


3

我迷失了最佳答案。

如果找不到任何内容,则Find返回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

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


1
一个STL插入版本会返回一个包含迭代器和布尔值的对。布尔值指示是否找到它,迭代器是找到的条目还是插入的条目。效率很难被击败;我说不可能。
Zan Lynx

4
不,使用了已检查的答案lower_bound,而不是find。结果,如果未找到键,则它将迭代器返回到插入点,而不是结尾。结果,它更快。
Steven Sudit


1

我似乎没有足够的要发表评论的地方,但是打勾的答案似乎很困扰我-当您考虑到无论如何insert都会返回迭代器时,为什么只能使用返回的迭代器来搜索Lower_bound。奇怪。


1
因为(肯定是C ++ 11之前的版本)使用插入意味着您仍然必须创建一个std::map::value_type对象,所以可接受的答案甚至可以避免这种情况。
KillianDS 2014年

-1

关于效率的任何答案将取决于STL的确切实现。唯一可以确定的方法是对这两种方法进行基准测试。我猜想差异不太可能很大,因此请根据您喜欢的样式进行选择。


1
这不是真的。STL与大多数其他库不同,它为大多数操作提供了明确的big-O要求。无论函数使用哪种实现来实现O(log n)行为,在2 * O(log n)和1 * O(log n)之间都有保证的差异。这个差异在您的平台上是否显着是另一个问题。但是差异永远存在。
srm

定义big-O要求的@srm仍然不能告诉您绝对需要多少时间。您所说的保证的差异不存在。
Mark Ransom

-2

map [key]-让stl对其进行分类。这最有效地传达了您的意图。

是的,足够公平。

如果先查找然后插入,则错过时您将执行2 x O(log N),因为查找仅使您知道是否需要插入,而不需要插入应该去的地方(lower_bound可能会帮助您解决问题) 。只需插入一条直线然后检查结果就是我要走的路。


否,如果该条目存在,它将返回对现有条目的引用。
克里斯·库姆勒

2
-1为这个答案。正如Kris K所说,使用map [key] = value将覆盖现有条目,而不是按问题的要求“保留”它。您无法使用map [key]测试是否存在,因为如果key不存在,它将返回默认的构造对象,并将其创建为key的条目
netjeff

重点是测试地图是否已填充,如果不存在则仅添加/覆盖。使用map [key]假定该值已经存在。
srm
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.