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.
#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
#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
#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);
#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.
#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