map

Eine Map enthält Wertepaare (siehe pair).

Die Typen des ersten und zweiten Elements in einem Paar können unterschiedlich sein.

Das erste Element im Paar wird als Schlüssel betrachtet, das zweite ist der zugehörige Wert. Eine Map könnte also Namen als Schlüssel und die zugehörigen Telefonnummern als Wert enthalten

Wertepaare werden komplett eingefügt (also immer ein Schlüssel und ein Wert zusammen).

Der Zugriff auf die Elemente erfolgt typischerweise entweder, indem anhand eines Schlüssel der zugehörige Wert gesucht wird (z.B. anhand eines Namens die passende Telefonnummer), oder indem alle Elemente durchlaufen werden und bei jedem Durchlauf ein Paar aus Schlüssel und Wert zum Vorschein kommt.

Eine Map ist also im Prinzip die Übersetzung der Werte einer Menge (Schlüssel, z.B. Namen) in jeweils einen Wert einer anderen Menge (Telefonnummer).

Für die Suche nach einem vorhandenen Element kann man entweder die Methode find() benutzen, oder den []-Operator wie bei einem Feldzugriff; allerdings ist der Suchschlüssel nicht unbedingt ganzzahlig, sondern natürlich vom Typ des Schlüssels.

Es ist vielleicht überraschend (weil sich Felder ganz anders verhalten), daß man sowohl mit [] als auch mit find() auf Elemente zugreifen kann, die bisher gar nicht existieren. Dann werden die Elemente umgehend angelegt (mit ihrem parameterlosen beziehungsweise ihrem Defaultkonstruktor).

Will man testen, ob ein Element existiert, geht man einen Umweg über einen Iterator, den man mit end() vergleichen kann. Wenn der Iterator gleich end() ist, dann verweist er auf kein gültiges Objekt.

Dazu ein kleines Beispielprogramm:

// Time-stamp: "05.01.06 11:24 map.cpp klaus@wachtler.de"
//
// demonstriert eine std::map
//
// 25.10.2004 kw   unter Linux entworfen und getestet, mit VC6 nicht
//                 kompilierbar
//
// 27.10.2004 kw   An VC6 (cl 12.00.8168) angepaßt unter Windows 2000:
//                  - zeigealter() gibt nicht mehr p.first aus,
//                    sondern p.first.c:str()
//                  - map bekommt als dritten Parameter ein
//                    Vergleichsargument für string (Typ stringcmp)
//                 Übersetzen: cl /EHsc map.cpp
//
// 29.10.2004 kw   Getestet mit MS VC++ 7.1 (VCToolkit),
//                 cl-Version 13.10.3077 unter Windows 2000
//                 Übersetzen: cl /EHsc map.cpp
//
//                 Außerdem mit devcpp (g++ 3.3.1 mingw) getestet
//                 unter Windows 2000.
//                 Übersetzen mit g++ -Wall map.cpp -o map.exe
//
//                 Nach wie vor unter Linux mit g++ 3.3.4 lauffähig
//                 (g++ -Wall map.cpp -o map)
//

#include <functional>
#include <algorithm>
#include <iostream>
#include <map>
#include <cstring>
#include <stdexcept>
#include <utility>


void zeigealter( const std::pair<std::string,int> p )
{
  std::cout << p.first.c_str() // c_str() ist nur für VC6 nötig; Linux
                               // bzw. g++ 3.3.2 kann strings direkt
                               // ausgeben (p.first).
            << " ist " << p.second
            << " Jahre alt"
            << std::endl;
}

// Für VC6 + VC7 nötig (sonst können strings in der map nicht verglichen
// werden):
class stringcmp
{
public:
  // Diese Funktion soll true liefern, wenn s1 kleiner als s2 ist.
  // Mit VC6 kann man leider strings nicht direkt vergleichen; deshalb
  // kommt die gute alte Funktion strcmp() zum Zuge:
  bool operator()( const std::string &s1, const std::string &s2 ) const
  {
    return strcmp( s1.c_str(), s2.c_str() ) < 0;
  }
}; // class stringcmp...

// Unter Linux (g++ 3.3.2) kann der dritte Parameter (stringcmp) der
// map entfallen:
//typedef std::map< const std::string, int > container;
typedef std::map< const std::string, int, stringcmp > container;

int main( int nargs, char **args )
{
  try
  {
    container    alter;

    // effektives Einfügen:
    alter.insert( std::make_pair(std::string( "Klaus" ), 42 ) );
    alter.insert( std::make_pair(std::string( "Otto" ), 12 ) );
    alter.insert( std::make_pair(std::string( "Fritz" ), 18 ) );
    alter.insert( std::make_pair(std::string( "Kuno" ), 56 ) );
    alter.insert( std::make_pair(std::string( "Gandalf" ), 99 ) );

    // geht auch zum Einfügen, aber langsamer:
    alter["Hund"] = 3;

    // Ausgabe aller Elemente der map:
    std::for_each( alter.begin(), alter.end(), zeigealter );

    // Zugriff auf ein vorhandenes Element über find():
    std::cout << "Fritz ist " << (alter.find("Fritz"))->second  << " alt"
              << std::endl;

    // Ebenso mit operator[]:
    std::cout << "Fritz ist " << alter["Fritz"]  << " alt"
              << std::endl;
    // "Fritzchen" existiert nicht; aber das ergibt keinen Fehler,
    // sondern es wird ein neues Element angelegt (mit dem Alter 0);
    // in dieser Form ist der Zugriff ziemlich unsinnig:
    std::cout << "Fritzchen ist " << alter["Fritzchen"]  << " alt"
              << std::endl;

    // Besser: Feststellen, ob es Fritzl gibt:
    container::iterator fritzl = alter.find("Fritzl");
    if( fritzl!=alter.end() )
    {
      // Wenn ja: ausgeben
      std::cout << "Fritzl ist "
                << fritzl->second  << " alt"
                << std::endl;
    }
    else
    {
      // Wenn nein: natürlich nicht ausgeben
      std::cout << "Fritzl gibt es nicht!" << std::endl;
    }
  }
  catch( std::exception &Fehler )
  {
    std::cerr << "Fehler: <" << Fehler.what() << "> in main()\n";
  }
  catch( ... )
  {
    std::cerr << "unbekannter Fehler in main()\n";
  }

  return 0;
} // main( int nargs, char **args )

Die Ausgabe davon:

Fritz ist 18 Jahre alt
Gandalf ist 99 Jahre alt
Hund ist 3 Jahre alt
Klaus ist 42 Jahre alt
Kuno ist 56 Jahre alt
Otto ist 12 Jahre alt
Fritz ist 18 alt
Fritz ist 18 alt
Fritzchen ist 0 alt
Fritzl gibt es nicht!

AnyWare@Wachtler.de