web-dev-qa-db-fra.com

Utilisation efficace de l'opérateur [] avec C ++ unordered_map

Premièrement, quelqu'un pourrait-il préciser si en C++ l'utilisation de l'opérateur [] en conjonction avec unmaped_map pour les recherches encapsule un appel à la méthode find (), ou utilise l'opérateur [] plus rapidement que find ()?

Deuxièmement, dans le morceau de code suivant, je soupçonne que dans les cas où la clé n'est pas déjà dans le unordered_map, j'effectue une deuxième recherche par le biais de la ligne map[key] = value afin de remplacer la valeur par défaut qui y est créée en utilisant l'opérateur [] lorsqu'aucune clé n'est présente.

Est-ce vrai, et dans l'affirmative, existe-t-il un moyen (peut-être en utilisant des pointeurs ou quelque chose) que je ne puisse effectuer qu'une seule recherche dans tous les cas (peut-être en stockant l'adresse de l'endroit où placer une valeur/lire une valeur) et toujours la même fonctionnalité? Évidemment, ce serait une amélioration de l'efficacité utile dans ce cas.

Voici l'extrait de code modifié:

    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;
36
Darius

operator[] insérera pour vous une entrée avec une valeur construite par défaut, s'il n'y en a pas déjà une. Il est équivalent à, mais sera probablement mis en œuvre plus efficacement que:

iterator iter = map.find(key);

if(iter == map.end())
{
    iter = map.insert(value_type(key, int())).first;
}

return *iter;

operator[] Peut être plus rapide que de faire le travail manuellement avec find() et insert() , car cela peut sauver d'avoir pour hacher à nouveau la clé.

Vous pouvez contourner plusieurs recherches dans votre code en prenant une référence à la valeur:

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;

Notez que si la valeur n'existe pas dans la carte, operator[] Construira par défaut et en insérera une. Ainsi, bien que cela évite les recherches multiples, cela pourrait en fait être plus lent s'il est utilisé avec un type qui est plus lent pour default-construct + assign que pour copy- ou move-construct.

Cependant, avec int, qui par défaut revient à 0, vous pourrez peut-être traiter 0 comme un nombre magique signifiant vide. Cela semble être le cas dans votre exemple.

Si vous n'avez pas un tel nombre magique, vous avez deux options. Ce que vous devez utiliser dépend du coût de calcul de la valeur.

Premièrement, lorsque le hachage de la clé est bon marché mais que le calcul de la valeur est coûteux, find() peut être la meilleure option. Cela hachera deux fois mais ne calculera la valeur qu'en cas de besoin:

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;

Mais si vous avez déjà la valeur, vous pouvez le faire très efficacement - peut-être un peu plus efficacement que d'utiliser un numéro de référence + magique comme ci-dessus:

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

Si le booléen renvoyé par map.insert(value_type) est vrai, l'élément a été inséré. Sinon, il existait déjà et aucune modification n'a été apportée. L'itérateur a renvoyé des points à la valeur insérée ou existante dans la carte. Pour votre exemple simple, cela peut être la meilleure option.

46
Cory Nelson

Vous pouvez à la fois vérifier si un élément existe, et insérer un nouvel élément s'il n'existe pas, avec la fonction spéciale insert qui retourne une pair<iterator, bool> dans lequel la valeur booléenne vous indique si la valeur a été réellement insérée. Par exemple, le code ici :

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

Le code insère ici la paire <'z',200> dans la carte si elle n'existe pas. Il renvoie l'itérateur où il est inséré si la valeur du deuxième élément de la paire retournée est vrai, ou, il retourne l'itérateur où l'élément était réellement, si le deuxième élément de la paire est faux.

10
Diego Sevilla

Premièrement, quelqu'un pourrait-il préciser si en C++ l'utilisation de l'opérateur [] en conjonction avec unmaped_map pour les recherches encapsule un appel à la méthode Find (), ou utilise l'opérateur [] plus rapidement que Find ()?

Il n'y a pas de règle pour cela. Une implémentation de [] Pourrait utiliser find(), elle pourrait effectuer la recherche par elle-même ou elle pourrait déléguer la recherche à une méthode privée qui est également utilisée par find() en interne.

Il n'y a également aucune garantie quant à celle qui est la plus rapide. find() implique une surcharge dans la construction et le retour d'un itérateur, tandis que [] sera probablement plus lent si la clé n'existe pas, car il insère une nouvelle valeur dans ce cas.

(...) existe-t-il un moyen (peut-être en utilisant des pointeurs ou quelque chose) que je ne puisse effectuer qu'une seule recherche dans tous les cas (...)

Si la clé n'est pas dans la carte, [] Insérera une nouvelle valeur construite par défaut et renverra ne référence. Par conséquent, vous pouvez stocker cette référence pour enregistrer la deuxième recherche:

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;
2
Ferdinand Beyer