Conteneurs associatifs - map

Décembre 2016

Conteneurs associatifs

Alors que les conteneurs de séquence sont conçus pour des accès séquentiels et aléatoires à leurs éléments via l’index ou un itérateur, les conteneurs associatifs sont conçus pour un accès aléatoire rapide aux éléments à l’aide de clés. La bibliothèque C++ standard fournit quatre conteneurs associatifs : map, multimap, set et multiset.

La bibliothèque standard distingue deux types de conteneurs associatifs : les conteneurs qui différencient la valeur de la clef de la valeur de l’objet lui-même et les conteneurs qui considèrent que les objets sont leur propre clef. Pour certains conteneurs, que l’on qualifie de conteneurs à clef unique, chaque élément contenu doit avoir une clef qui lui est propre. Il est donc impossible d’insérer plusieurs éléments distincts avec la même clef dans ces conteneurs (map, set). En revanche, les conteneurs associatifs dits à clefs multiples permettent l’utilisation d’une même valeur de clef pour plusieurs objets distincts. L’opération de recherche d’un objet à partir de sa clef peut donc, dans ce cas, renvoyer plus de un objet (multimap, multiset).

map

Le conteneur map permet de stocker « des choses » comme un tableau, mais à la grande différence de ce dernier, sa taille évolue dynamiquement ! Vous ne serez limité que par la mémoire de votre ordinateur.
Une autre grande différence des map sur les tableaux est la façon dont ils sont indexés. L’index peut être de type int, char, string, ou n’importe quelle autre classe que vous aurez définie, du moment qu’il existe un moyen de comparer les objets.

À savoir

La façon particulière dont les map sont indexés entraîne une surcharge de traitement lors des opérations d’insertion et de tri. Si les performances du programme sont essentielles, vous devrez envisager d’écrire votre propre code pour ces fonctions.
Pour utiliser un conteneur map, vous devez inclure le fichier en-tête <map> au début du programme et travailler dans l’espace de noms std.
Chaque élément d’un map est une paire struct définie dans l’espace de noms std comme suit :
template<class First, class Second>  struct  pair 
{ 
  // Autres membres de structure 

  First  first; 
  Second  second; 
};


Comme les conteneurs de séquence, la classe map fournit les trois fonctions membres size(), max_size() empty() liées à la taille. Elle fournit aussi les itérateurs begin() (pointe sur le premier élément), end() (pointe vers l’élément après le dernier), rbegin() (pointe sur le premier élément de la séquence inverse), et rend() (pointe vers l’élément après le dernier de la séquence inverse).
La classe map fournit aussi l’opérateur index [] pour accéder directement aux éléments et la fonction find() pour retrouver un élément à partir de sa clé. Celle-ci renvoie map::end() si la clé n’existe pas.
Notre exemple de code illustre les principales opérations réalisables sur un conteneur.

Code 8.13 : utilisation d’un map

#include <iostream> 
#include <string> 
#include <map> 
using namespace std; 

class MaClasse 
{ 
public: 
   //constructeurs 
    MaClasse():v1("Objet nouveau"), v2(0)  {} 
    MaClasse(string valeur1, int valeur2 = 0): 
      v1(valeur1), v2(valeur2)  {} 

  //Fonctions d’accès 
  void    DefinirValeur(string valeur1)  { v1  = valeur1;  } 
  void    DefinirValeur(int valeur2)  { v2  = valeur2;  } 
  string LireValeur() const { return v1; } 
  int    LireValeur2() const { return v2; } 

  //opérateur << surchargé 
  friend ostream& operator<<(ostream& sortie, const MaClasse& p) 
  { 
    sortie  << "Chaîne: " << p.v1 
      << "\tValeur: " << p.v2; 
    return sortie; 
  } 

private: 
  string  v1; 
  int  v2; 
}; 

// Fonction modèle d’affichage des éléments d’un map 
template<class T, class A> 
void AfficherMap(const map<T, A>& m); 

int main() 
{ 
  //On crée 3 objets 
  MaClasse objet1("Objet 1", 10); 
  MaClasse objet2("Objet 2", 20); 
  MaClasse objet3("Objet 3", 30); 

  //On crée le map 
  map<string, MaClasse> MonMap; 
  //On initialise les éléments 
  MonMap[objet1.LireValeur()] = objet1; 
  MonMap[objet2.LireValeur()] = objet2; 
  MonMap[objet3.LireValeur()] = objet3; 

  AfficherMap(MonMap); 

    //On affiche la valeur de objet1 
  cout << "Voici les valeurs de objet1: " << MonMap["Objet 1"].LireValeur(); 
  cout << "\t" << MonMap["Objet 1"].LireValeur2() << endl; 

  //On modifie les 2 valeurs de objet2 
  MonMap["Objet 2"].DefinirValeur("Objet 2 bis"); 
  MonMap["Objet 2"].DefinirValeur(22); 
  cout << "Voici les nouvelles valeurs de objet2: "; 
  cout << MonMap["Objet 2"].LireValeur() << "\t"; 
  cout << MonMap["Objet 2"].LireValeur2() << "\n"; 

  cout << "On ajoute au map : Chaîne: Objet 1 Valeur: 40\n"; 
  MaClasse objet4("Objet 1", 40); 
  MonMap[objet4.LireValeur()] = objet4; 
  AfficherMap(MonMap); 

  // Les diverses fonctions d’accès relatives à l’objet 1 
  cout << "\nVoici le nombre d’objets 1 : "; 
  cout << MonMap.count("Objet 1") << "\n"; 

  //On cherche le premier objet de clé "Objet 1" 
  map<string, MaClasse>::iterator ci=MonMap.lower_bound("Objet 1"); 
  cout << "Le premier Objet 1: " << ci->second << "\n"; 

  //on pointe sur l’élément suivant 
  ci = MonMap.upper_bound("Objet 1"); 
  cout << "Elément qui suit Objet 1: " << ci->second; 

  // On tente d’accéder à un élément qui n’existe pas 
  cout << "\nOn cherche un Objet 10 : "; 
  cout << MonMap["Objet 10"] << endl; 

  cout << "On efface Objet 3\n"; 
  MonMap.erase("Objet 3"); 
  AfficherMap(MonMap); 

  cout << "On efface tout\n"; 
  MonMap.clear(); 
  AfficherMap(MonMap); 

} 

//Définition de la fonction d’affichage du map 
template<class T, class A> 
void AfficherMap(const map<T, A>& m) 
{ 
  cout << "Voici les éléments du map :\n"; 
  for (typename map<T, A>::const_iterator ci = m.begin(); ci != m.end(); ++ci) 
  cout << ci->first << "\t" << ci->second << "\n"; 

  cout << "\n\n"; 
}


En exécutant ce programme, vous obtenez le résultat suivant :

Voici les éléments du map : 
Objet 1   Chaîne: Objet 1   Valeur: 10 
Objet 2   Chaîne: Objet 2   Valeur: 20 
Objet 3   Chaîne: Objet 3   Valeur: 30 


Voici les valeurs de objet1: Objet 1   10 
Voici les nouvelles valeurs de objet2: Objet 2 bis   22 
On ajoute au map : Chaîne: Objet 1 Valeur: 40 
Voici les éléments du map : 
Objet 1   Chaîne: Objet 1   Valeur: 40 
Objet 2   Chaîne: Objet 2 bis   Valeur: 22 
Objet 3   Chaîne: Objet 3   Valeur: 30 



Voici le nombre d’objets 1 : 1 
Le premier Objet 1: Chaîne: Objet 1   Valeur: 40 
Elément qui suit Objet 1: Chaîne: Objet 2 bis   Valeur: 22 
On cherche un Objet 10 : Chaîne: Objet nouveau   Valeur: 0 
On efface Objet 3 
Voici les éléments du map : 
Objet 1   Chaîne: Objet 1   Valeur: 40 
Objet 10   Chaîne: Objet nouveau   Valeur: 0 
Objet 2   Chaîne: Objet 2 bis   Valeur: 22 


On efface tout 
Voici les éléments du map :


Les éléments d’un tel conteneur étant des paires, nous utilisons first et second pour renvoyer leur paire clé/valeur. Vous constatez qu’après l’ajout d’un second objet de clé Objet 1, celui-ci remplace le premier puisqu’un map ne peut contenir de doublons sur la clé.
Notez aussi que nous avons fait en sorte que la recherche d’un élément inexistant entraîne la création d’un nouvel élément par l’appel du constructeur par défaut de la classe.

Le texte original de cette fiche pratique est extrait de
«Tout sur le C++» (Christine EBERHARDT, Collection
CommentCaMarche.net, Dunod, 2009)

A voir également :

Ce document intitulé «  Conteneurs associatifs - map  » issu de CommentCaMarche (www.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.