我正在使用STL地图数据结构,目前我的代码首先调用 find():如果该键之前未在地图中,则调用 insert() 它,否则什么都不做。

map<Foo*, string>::iterator it;
it = my_map.find(foo_obj);   // 1st lookup

if(it == my_map.end()){
  my_map[foo_obj] = "some value";  // 2nd lookup
}else{
  // ok do nothing.
}

我想知道是否有比这更好的方法,因为据我所知,在这种情况下,当我想插入一个尚未出现的密钥时,我在地图数据结构中执行2次查找:1 find()中的一个, insert()中的一个(对应于 operator [] )。

提前感谢任何建议。

有帮助吗?

解决方案

通常,如果您执行查找并且可能是插入,那么您希望保留(并检索)旧值(如果已存在)。如果您只想覆盖任何旧值, map [foo_obj] =&quot; some value&quot; 就会这样做。

以下是如何通过一次地图查找获取旧值或插入新值(如果不存在):

typedef std::map<Foo*,std::string> M;
typedef M::iterator I;
std::pair<I,bool> const& r=my_map.insert(M::value_type(foo_obj,"some value"));
if (r.second) { 
    // value was inserted; now my_map[foo_obj]="some value"
} else {
    // value wasn't inserted because my_map[foo_obj] already existed.
    // note: the old value is available through r.first->second
    // and may not be "some value"
}
// in any case, r.first->second holds the current value of my_map[foo_obj]

这是一个很常见的习惯用法,你可能想要使用辅助函数:

template <class M,class Key>
typename M::mapped_type &
get_else_update(M &m,Key const& k,typename M::mapped_type const& v) {
    return m.insert(typename M::value_type(k,v)).first->second;
}

get_else_update(my_map,foo_obj,"some value");

如果你想要跳过一个昂贵的v计算(如果它已经存在)(例如memoization),你也可以概括一下:

template <class M,class Key,class F>
typename M::mapped_type &
get_else_compute(M &m,Key const& k,F f) {
   typedef typename M::mapped_type V;
   std::pair<typename M::iterator,bool> r=m.insert(typename M::value_type(k,V()));
   V &v=r.first->second;
   if (r.second)
      f(v);
   return v;
}

例如

struct F {
  void operator()(std::string &val) const 
  { val=std::string("some value")+" that is expensive to compute"; }
};
get_else_compute(my_map,foo_obj,F());

如果映射类型不是默认可构造的,则make F提供默认值,或者为get_else_compute添加另一个参数。

其他提示

主要有两种方法。第一种方法是使用带有值类型的insert函数,它返回一个迭代器和一个bool,它指示是否发生插入并将迭代器返回给具有相同键或新插入元素的现有元素。

map<Foo*, string>::iterator it;
it = my_map.find(foo_obj);   // 1st lookup

my_map.insert( map<Foo*, string>::value_type(foo_obj, "some_value") );

这样做的好处是它很简单。主要缺点是无论是否需要插入,总是为第二个参数构造一个新值。在字符串的情况下,这可能无关紧要。如果你的价值昂贵,那么这可能比必要的更浪费。

这样做的方法是使用插入的'提示'版本。

std::pair< map<foo*, string>::iterator, map<foo*, string>::iterator >
    range = my_map.equal_range(foo_obj);

if (range.first == range.second)
{
    if (range.first != my_map.begin())
        --range.first;

    my_map.insert(range.first, map<Foo*, string>::value_type(foo_obj, "some_value") );
}

只有在提供的迭代器之后立即插入元素,因此插入g才能保证在分摊的常量时间内,如果可能的话, -

修改

如果 - 的需要看起来很奇怪,那就是。标准中有一个开放缺陷(233)可以解决这个问题,尽管在重复的问题 map 的问题描述更加清晰。 dkuug.dk/jtc1/sc22/wg21/docs/lwg-closed.html#246"rel =“noreferrer”> 246

在您的示例中,您希望在找不到时插入。如果默认构造并在此之后设置值并不昂贵,我建议使用1个查找更简单的版本:

string& r = my_map[foo_obj];    // only lookup & insert if not existed
if (r == "") r = "some value";  // if default (obj wasn't in map), set value
                                // else existed already, do nothing

如果您的示例告诉您实际需要什么,请考虑将该值添加为 str Foo :: s ,而您已经拥有该对象,因此不需要查找,只需检查它是否具有默认值该成员的价值。并将objs保存在 std :: set 中。即使扩展类FooWithValue2 也可能比使用 map 更便宜。

但是如果真的需要通过这样的地图加入数据,或者如果你想仅在它存在的情况下进行更新,那么Jonathan就有了答案。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top