在c++ unordered_map中高效地使用[]操作符

Using The [] Operator Efficiently With C++ unordered_map

本文关键字:操作符 高效 unordered c++ map      更新时间:2023-10-16

首先,有人可以澄清是否在c++中使用[]操作符与unordered_map一起用于查找包装了对find()方法的调用,或者使用[]操作符比find()更快?

其次,在下面的代码段中,我怀疑在键不在unordered_map中的情况下,我通过map[key] = value行执行第二次查找,以便在键不存在时使用[]操作符替换在那里创建的默认值。

是真的吗?如果是这样,是否有一种方法(也许是通过使用指针或其他东西),我可能只执行一次查找在任何情况下(也许是通过存储地址的地方放置一个值/读取一个值),仍然实现相同的功能?显然,如果这样的话,这将是一个有用的效率提高。

下面是修改后的代码摘录:

    int stored_val = map[key]; // first look up. Does this wrap ->find()??
    // return the corresponding value if we find the key in the map - ie != 0
    if (stored_val) return stored_val;
    // if not in map
    map[key] = value; 
       /* second (unnecessary?) look up here to find position for newly 
          added key entry */
   return value;

operator[]将为您插入一个带有默认构造值的条目(如果没有的话)。它相当于

,但实现起来可能比 更有效:
iterator iter = map.find(key);
if(iter == map.end())
{
    iter = map.insert(value_type(key, int())).first;
}
return *iter;

operator[]可以比手动使用find()insert()更快,因为它可以节省重新散列密钥的时间。

在代码中处理多次查找的一种方法是获取对值的引用:

int &stored_val = map[key];
// return the corresponding value if we find the key in the map - ie != 0
if (stored_val) return stored_val;
// if not in map
stored_val = value;
return value;

注意,如果该值在映射中不存在,operator[]将默认构造并插入一个。因此,虽然这将避免多次查找,但如果与默认构造+赋值比复制或移动构造慢的类型一起使用,它实际上可能会更慢。

对于int,它便宜地默认构造为0,您可以将0视为表示空的幻数。这看起来就像您的示例中的情况。

如果你没有这样的神奇数字,你有两个选择。你应该使用什么取决于你计算这个值的成本有多高。

首先,当散列键很便宜但计算值很昂贵时,find()可能是最好的选择。这将哈希两次,但只在需要时计算值:

iterator iter = map.find(key);
// return the corresponding value if we find the key in the map
if(iter != map.end()) return *iter;
// if not in map
map.insert(value_type(key, value));
return value;

但是如果你已经得到了值,你可以非常有效地做到这一点——也许比上面使用引用+幻数更有效:

pair<iterator,bool> iter = map.insert(value_type(key, value));
return *iter.first;

如果map.insert(value_type)返回的bool值为true,则表示插入了该项。否则,它已经存在,没有做任何修改。迭代器返回指向映射中插入值或现有值的点。对于您的简单示例,这可能是最好的选择。

可以检查元素是否存在,如果不存在则插入一个新元素,使用特殊的insert函数返回一个pair<iterator, bool>,其中布尔值告诉您该值是否已实际插入。例如,下面的代码:

  unordered_map<char, int> mymap;
  pair<unordered_map<char,int>::iterator,bool> ret;
  // first insert function version (single parameter):;
  mymap.insert ( pair<char,int>('z',200) );
  ret=mymap.insert (pair<char,int>('z',500) ); 
  if (ret.second==false)
  {
    cout << "element 'z' already existed";
    cout << " with a value of " << ret.first->second << endl;
  }

如果配对<'z',200>不存在,这里的代码将其插入到映射中。如果返回的元素对的第二个元素的值为真,则返回插入元素的迭代器;如果返回的元素对的第二个元素为假,则返回元素实际所在的迭代器。

首先,有人可以澄清是否在c++中使用[]操作符与unordered_map一起用于查找包装了对Find()方法的调用,或者使用[]操作符比Find()更快?

没有这样的规则。[]的实现可以使用find(),它可以自己执行查找,也可以将查找委托给find()内部使用的某个私有方法。

也不能保证哪一个更快。find()涉及构造和返回迭代器的开销,而[]在键不存在的情况下可能会慢一些,因为在这种情况下它会插入一个新值。

(…)是否有一种方法(可能是通过使用指针或其他东西),我可能只执行一次查找在任何情况下(…)

如果键不在映射中,[]将插入一个新的默认构造值,并返回一个引用。因此,您可以存储该引用以保存第二次查找:

int& stored_val = map[key];  // Note the reference
if (stored_val) return stored_val;
// Use the reference to save a second lookup.
stored_val = value; 
return value;