代码改变世界

STL map and multimap

2011-08-12 10:02  Daniel Zheng  阅读(393)  评论(0编辑  收藏  举报

A Brief Introduction

The map and multimap are key-value pair containers that allow for a lookup on the basis of a key. The difference between the map and multimap is that only the latter allows for duplicates, whereas the former can  store only unique keys.

Th facilitate quick searching, STL implementations of the map and multimap internally look like a binary tree. This means that elements inserted in a map or a multimap are sorted on insertion. It also means that, unlike a vector where elements at a position can be replaced by another, elements in a map at a givens position cannot be replaced by a new element of a different value. This is because the map would ideally like to have it placed in a possible different location in accordance with its value relative to those in the internal tree.

Basic STL map and multimap Operations


Instantiating a std::map Object

The template instantiation of the map class needs the programmer to specify the key type, the value type, and optionally a predicate that helps the map class to sort the elements on insertion.

View Code
#include <map>
#include
<string>

int main()
{
using namespace std;

map
<int, string> mapIntegersToString;
multimap
<int, string> mmapIntegersToString;
}

  

Inserting Elements in an STL map or multimap

View Code
#include <map>
#include
<iostream>

using namespace std;

// Type-define the map and multimap definition for easy readability
typedef map <int, string> MAP_INT_STRING;
typedef multimap
<int, string> MMAP_INT_STRING;

int main ()
{
MAP_INT_STRING mapIntToString;

// Insert key-value pairs into the map using value_type
mapIntToString.insert (MAP_INT_STRING::value_type (3, "Three"));

// Insert a pair using function make_pair
mapIntToString.insert (make_pair (-1, "Minus One"));

// Insert a pair object directly
mapIntToString.insert (pair <int, string> (1000, "One Thousand"));

// Insert using an array-like syntax for inserting key-value pairs
mapIntToString [1000000] = "One Million";

cout
<< "The map contains " << mapIntToString.size ();
cout
<< " key-value pairs. " << endl;
cout
<< "The elements in the map are: " << endl;

// Print the contents of the map to the screen
MAP_INT_STRING::const_iterator iMapPairLocator;

for ( iMapPairLocator = mapIntToString.begin ()
; iMapPairLocator
!= mapIntToString.end ()
;
++ iMapPairLocator )
{
cout
<< "Key: " << iMapPairLocator->first;
cout
<< " Value: " << iMapPairLocator->second.c_str ();

cout
<< endl;
}

MMAP_INT_STRING mmapIntToString;

// The insert function works the same way for multimap too
mmapIntToString.insert (MMAP_INT_STRING::value_type (3, "Three"));
mmapIntToString.insert (MMAP_INT_STRING::value_type (
45, "Forty Five"));
mmapIntToString.insert (make_pair (
-1, "Minus One"));
mmapIntToString.insert (pair
<int, string> (1000, "One Thousand"));

// A multimap can store duplicates - insert one
mmapIntToString.insert (MMAP_INT_STRING::value_type (1000, "Thousand"));

cout
<< endl << "The multimap contains " << mmapIntToString.size ();
cout
<< " key-value pairs." << endl;
cout
<< "The elements in the multimap are: " << endl;

// Print the contents of the map to the screen
MMAP_INT_STRING::const_iterator iMultiMapPairLocator;

for ( iMultiMapPairLocator = mmapIntToString.begin ()
; iMultiMapPairLocator
!= mmapIntToString.end ()
;
++ iMultiMapPairLocator )
{
cout
<< "Key: " << iMultiMapPairLocator->first;
cout
<< " Value: " << iMultiMapPairLocator->second.c_str ();

cout
<< endl;
}

cout
<< endl;

// The multimap can also return the number of pairs with the same key
cout << "The number of pairs in the multimap with 1000 as their key: "
<< mmapIntToString.count (1000) << endl;

return 0;
}

Output:

  The map contains 4 key-value pairs.

  The elements in the map are:

  Key: -1 Value: Minus One

  Key: 3 Value: Three

  Key: 1000 Value: One Thousand

  Key: 1000000 Value: One Million

  The multimap contains 5 key-value pairs.

  The elements in the multimap are:

  Key: -1 Value: Minus One

  Key: 3 Value: Three

  Key: 45 Value: Forty Five

  Key: 1000 Value: One Thousand

  Key: 1000 Value: One Thousand

  The number of pairs in the multimap with 1000 as their key are: 2

Finding Elements in an STL map or multimap

View Code
#include <map>
#include
<iostream>
#include
<string>

using namespace std;

// Typedef the multimap definition for easy readability
typedef multimap <int, string> MMAP_INT_STRING;

int main ()
{
MMAP_INT_STRING mmapIntToString;

// The insert function works the same way for multimap too
mmapIntToString.insert (MMAP_INT_STRING::value_type (3, "Three"));
mmapIntToString.insert (MMAP_INT_STRING::value_type (
45, "Forty Five"));
mmapIntToString.insert (MMAP_INT_STRING::value_type (
-1, "Minus One"));
mmapIntToString.insert (MMAP_INT_STRING::value_type (
1000, "Thousand"));

// A multimap can store duplicates - insert one
mmapIntToString.insert (MMAP_INT_STRING::value_type
(
1000, "Thousand (duplicate)"));

cout
<< "The multimap contains " << mmapIntToString.size ();
cout
<< " key-value pairs." << endl;
cout
<< "The elements in the multimap are: " << endl;

// Print the contents of the map to the screen
MMAP_INT_STRING::const_iterator iMultiMapPairLocator;

for ( iMultiMapPairLocator = mmapIntToString.begin ()
; iMultiMapPairLocator
!= mmapIntToString.end ()
;
++ iMultiMapPairLocator )
{
cout
<< "Key: " << iMultiMapPairLocator->first;
cout
<< ", Value: " << iMultiMapPairLocator->second << endl;
}

cout
<< endl;

cout
<< "Finding all key-value pairs with 1000 as their key: " << endl;

// Find an element in the multimap using the 'find' function
MMAP_INT_STRING::const_iterator iElementFound;

iElementFound
= mmapIntToString.find (1000);

// Check if "find" succeeded
if (iElementFound != mmapIntToString.end ())
{
// Find the number of pairs that have the same supplied key
size_t nNumPairsInMap = mmapIntToString.count (1000);
cout
<< "The number of pairs in the multimap with 1000 as key: ";
cout
<< nNumPairsInMap << endl;

// Output those values to the screen
cout << "The values corresponding to the key 1000 are: " << endl;
for ( size_t nValuesCounter = 0
; nValuesCounter
< nNumPairsInMap
;
++ nValuesCounter )
{
cout
<< "Key: " << iElementFound->first;
cout
<< ", Value [" << nValuesCounter << "] = ";
cout
<< iElementFound->second << endl;

++ iElementFound;
}
}
else
cout
<< "Element not found in the multimap";

return 0;
}

Output:

  The multimap contains 5 key-value pairs.

  The elements in the multimap are:

  Key: -1, Value: Minus One

  Key: 3, Value: Three

  Key: 45, Value: Forty Five

  Key: 1000, Value: Thousand

  Key: 1000, Value: Thousand (duplicate)

  Finding all pairs with 1000 as their key...

  The number of pairs in the multimap with 1000 as key: 2

  The values corresponding to the key 1000 are:

  Key: 1000, Value [0] = Thousand

  Key: 1000, Value [1] = Thousand (duplicate)

Erasing Elements from an STL map or multimap

The erase function is invoked with the key as the parameter to delete all pairs with a certain key:

mapObject.erase (key);

Another form of the erase function allows the deletion of a particular element given an iterator that points to it:

mapObject.erase (iElement);

You can erase a range of elements from a map or a multimap using iterators that supply the bounds:

mapObject.erase (iLowerBound, iUpperBound);

View Code
#include <map>
#include
<iostream>
#include
<string>

using namespace std;

// typedef the multimap definition for easy readability
typedef multimap <int, string> MULTIMAP_INT_STRING;

int main ()
{
MULTIMAP_INT_STRING mmapIntToString;

// Insert key-value pairs into the multimap
mmapIntToString.insert (MULTIMAP_INT_STRING::value_type (3, "Three"));
mmapIntToString.insert (MULTIMAP_INT_STRING::value_type(
45, "Forty Five"));
mmapIntToString.insert (MULTIMAP_INT_STRING::value_type (
-1, "Minus One"));
mmapIntToString.insert (MULTIMAP_INT_STRING::value_type (
1000, "Thousand"));

// Insert duplicates into the multimap
mmapIntToString.insert (MULTIMAP_INT_STRING::value_type (-1, "Minus One"));
mmapIntToString.insert (MULTIMAP_INT_STRING::value_type (
1000, "Thousand"));

cout
<< "The multimap contains " << mmapIntToString.size ();
cout
<< " key-value pairs. " << "They are: " << endl;

// Print the contents of the multimap to the screen
MULTIMAP_INT_STRING::const_iterator iPairLocator;

for ( iPairLocator = mmapIntToString.begin ()
; iPairLocator
!= mmapIntToString.end ()
;
++ iPairLocator )
{
cout
<< "Key: " << iPairLocator->first;
cout
<< ", Value: " << iPairLocator->second.c_str () << endl;
}

cout
<< endl;

// Eraseing an element with key as -1 from the multimap
if (mmapIntToString.erase (-1) > 0)
cout
<< "Erased all pairs with -1 as key." << endl;

// Erase an element given an iterator from the multimap
MULTIMAP_INT_STRING::iterator iElementLocator = mmapIntToString.find(45);
if (iElementLocator != mmapIntToString.end ())
{
mmapIntToString.erase (iElementLocator);
cout
<< "Erased a pair with 45 as key using an iterator" << endl;
}

// Erase a range from the multimap...
cout << "Erasing the range of pairs with 1000 as key." << endl;
mmapIntToString.erase ( mmapIntToString.lower_bound (
1000)
, mmapIntToString.upper_bound (
1000) );

cout
<< endl;
cout
<< "The multimap now contains " << mmapIntToString.size ();
cout
<< " key-value pair(s)." << "They are: " << endl;

// Print the contents of the multimap to the screen
for ( iPairLocator = mmapIntToString.begin ()
; iPairLocator
!= mmapIntToString.end ()
;
++ iPairLocator )
{
cout
<< "Key: " << iPairLocator->first;
cout
<< ", Value: " << iPairLocator->second.c_str () << endl;
}

return 0;
}

Output:

  The multimap contains 6 key-value pairs. They are:

  Key: -1, Value: Minus One

  Key: -1, Value: Minus One

  Key: 3, Value: Three

  Key: 45, Value: Forty Five

  Key: 1000, Value: Thousand

  Key: 1000, Value: Thousand

  Erased all pairs with -1 as key.

  Erased a pair with 45 as key using an iterator

  Erasing the range of pairs with 1000 as key.

  The multimap now contains 1 key-value pair(s).They are:

  Key: 3, Value: Three

Supplying a Custom Sort Predicate

The map and multimap template definition includes a third parameter that accepts the sort predicate for the map to function correctly. This third parameter, when not mentioned (as in the preceding examples), is substituted with the default sort criterion provided by std::less <>, which essentially compares two objects using the < operator.

View Code
#include <map>
#include
<algorithm>
#include
<string>
#include
<iostream>

using namespace std;

/*
This is the binary predicate that helps the map sort
string-keys irrespective of their case
*/
struct CCaseInsensitive
{
bool operator () (const string& str1, const string& str2) const
{
string str1NoCase (str1), str2NoCase (str2);
transform (str1.begin(), str1.end(), str1NoCase.begin(), tolower);
transform (str2.begin(), str2.end(), str2NoCase.begin(), tolower);

return (str1NoCase < str2NoCase);
};
};


// Typedef map definitions for easy readability...
// A directory that sorts keys using string::operator < (case sensitive)
typedef map <string, string> DIRECTORY_WITHCASE;

// A case-insensitive directory definition
typedef map <string, string, CCaseInsensitive> DIRECTORY_NOCASE;

int main ()
{
// Case-insensitive directory: case of the string-key plays no role
DIRECTORY_NOCASE dirNoCase;

dirNoCase.insert (DIRECTORY_NOCASE::value_type (
"John", "2345764"));
dirNoCase.insert (DIRECTORY_NOCASE::value_type (
"JOHN", "2345764"));
dirNoCase.insert (DIRECTORY_NOCASE::value_type (
"Sara", "42367236"));
dirNoCase.insert (DIRECTORY_NOCASE::value_type (
"Jack", "32435348"));

cout
<< "Displaying contents of the case-insensitive map:" << endl;

// Print the contents of the map to the screen
DIRECTORY_NOCASE::const_iterator iPairLocator1;
for ( iPairLocator1 = dirNoCase.begin()
; iPairLocator1
!= dirNoCase.end()
;
++ iPairLocator1 )
{
cout
<< "Name: " << iPairLocator1->first;
cout
<< ", Phone number: " << iPairLocator1->second << endl;
}

cout
<< endl;

// Case-sensitive directory: case of the string-key affects
// insertion & search
DIRECTORY_WITHCASE dirWithCase;

// Take sample values from previous map...
dirWithCase.insert ( dirNoCase.begin(), dirNoCase.end() );

cout
<< "Displaying contents of the case-sensitive map:" << endl;

// Print the contents of the map to the screen
DIRECTORY_WITHCASE::const_iterator iPairLocator2;
for ( iPairLocator2 = dirWithCase.begin()
; iPairLocator2
!= dirWithCase.end()
;
++ iPairLocator2 )
{
cout
<< "Name: " << iPairLocator2->first;
cout
<< ", Phone number: " << iPairLocator2->second << endl;
}

cout
<< endl;

// Search for a name in the two maps and display result
cout << "Please enter a name to search: " << endl << "> ";
string strNameInput;
cin
>> strNameInput;

DIRECTORY_NOCASE::const_iterator iSearchResult1;

// find in the map...
iSearchResult1 = dirNoCase.find (strNameInput);
if (iSearchResult1 != dirNoCase.end())
{
cout
<<iSearchResult1->first<< "'s number in the case-insensitive";
cout
<< " directory is: " << iSearchResult1->second << endl;
}
else
{
cout
<< strNameInput << "'s number not found ";
cout
<< "in the case-insensitive directory" << endl;
}

DIRECTORY_WITHCASE::const_iterator iSearchResult2;

// find in the case-sensitive map...
iSearchResult2 = dirWithCase.find (strNameInput);
if (iSearchResult2 != dirWithCase.end())
{
cout
<< iSearchResult2->first<< "'s number in the case-sensitive";
cout
<< " directory is: " << iSearchResult2->second << endl;
}
else
{
cout
<< strNameInput << "'s number was not found ";
cout
<< "in the case-sensitive directory" << endl;
}

return 0;
}

Output:

  Displaying the contents of the case-insensitive map on the screen...

  Name: Jack, Phone number: 32435348

  Name: John, Phone number: 2345764

  Name: Sara, Phone number: 42367236

  Displaying the contents of the case-sensitive map on the screen...

  Name: Jack, Phone number: 32435348

  Name: John, Phone number: 2345764

  Name: Sara, Phone number: 42367236

  Please enter a name to search in the directories:

  > JOHN

  John’s number from the case-insensitive directory is: 2345764