一、map下標操作所導致的初始化(副作用)
1、調用值類型mapped_type的默認構造函數來初始化與該鍵關聯的值,再將這個鍵值對插入到map中;2、然後再在新的map中獲取該鍵關聯的值來執行用戶的操作。
如下代碼:
void main()
{
map<string, int> my_words;
my_words.insert(make_pair("liz1", 1));
my_words.insert(make_pair("liz2", 2));
int count = my_words["liz3"];
cout<<count<<endl;
cout<<my_words.count("liz3")<<endl;
system("pause");
}
當執行my_words["liz3"]操作時,先獲得int的默認初始值0(如果爲類類型則爲默認構造函數),然後與liz3構成一個新的pair<string, int>類型的鍵值對,並將該鍵值對插入到my_words中,然後在在新的map中獲取到鍵"liz3"所關聯的值,此時應該爲0了。
二、map的三種insert操作及其返回值的作用
1、map容器不需要我們標註插入的位置。這與map容器的底層結構有關係。map的實現數據結構爲紅黑樹,紅黑樹具有二叉排序樹的性質,能夠根據需要插入的值來尋找插入的位置。2、map中的鍵總是唯一不重複的,因此,對於map中已經存在的鍵則需要直接忽視。
接下來我們詳解一下map的三種插入操作:
1、map.insert(value)
- 當鍵不存在時,我們將value直接插入map。與前面的下標操作相比較,無需進行任何的初始化操作;
- 對於insert的返回值,是一個pair類型,包括一個迭代器和一個bool對象。此迭代器指向的是map中鍵爲value.first的元素,bool標識是否插入了value。假設value.first在map中已經存在了,則迭代器指向map中已經存在的鍵爲value.first的元素,bool設置爲false,表示沒有發生插入操作;假設value.first在map中不存在,則先插入,然後再將迭代器指向該元素,bool設置爲true。下面以兩段程序來說明這點。
void main()
{
map<string, int> my_words;
my_words.insert(make_pair("liz1", 1));
my_words.insert(make_pair("liz2", 2));
pair<map<string, int>::iterator, bool> returnPtr = my_words.insert(make_pair("liz1", 8)); //插入的鍵在my_words中已經存在
cout<<returnPtr.first->first<<" "<<returnPtr.first->second<<endl; //迭代器指向的是my_words中已經存在的鍵爲liz1的鍵值對
cout<<"insert happen? : "<<returnPtr.second<<endl; //沒有發生插入操作,bool應該爲false
system("pause");
}
運行結果爲:
首先看程序,my_words中已經存在了鍵爲liz1的鍵值對,當試圖插入一個鍵爲liz1的鍵值對時,my_words將保持不變,並且給函數返回一個pair類型的返回值,包含一個指向my_words中已經存在的鍵值對,另一個爲false,表明沒有發生插入操作。
void main()
{
map<string, int> my_words;
my_words.insert(make_pair("liz1", 1));
my_words.insert(make_pair("liz2", 2));
pair<map<string, int>::iterator, bool> returnPtr = my_words.insert(make_pair("liz3", 8)); //插入的鍵在my_words中不存在
cout<<returnPtr.first->first<<" "<<returnPtr.first->second<<endl; //迭代器指向的插入之後鍵爲liz3的鍵值對
cout<<"insert happen? : "<<returnPtr.second<<endl; //發生了插入操作,bool爲true
system("pause");
}
首先看程序,插入的鍵值對中鍵爲liz3,這個鍵在my_words中不存在,則insert函數會將該鍵值對插入my_words中,然後再將已經插入my_words中的該鍵值對的迭代器返回,並且bool值爲true。
2、map.insert(beg, end)
3、map.insert(iter, value)(指定查找起點)
對於第一個insert函數,在嘗試插入value時,需要通過value的鍵來尋找其插入位置,回憶平衡二叉樹的查找過程,總是從根結點開始比較,一直往下查找直到葉子結點。這裏,假如現在的map的二叉樹的深度非常大,其值是從1到10000,然後我現在要插入的元素是9999,並且我們已經直到了8000已經插入了,那麼,我們肯定是從8000結點開始查找,而不是傻傻地還從0開始查找。
下面通過一段程序來測試一下這第一個和第三個insert的運行時間,按照理論,第三個insert的效率要高一些,運行時間短一些。程序如下:
/*
在for循環中執行insert操作是希望將時間可見,否則總是爲0
*/
void main()
{
map<int, int> my_map;
pair<map<int, int>::iterator, bool> returnPtr;
for(long i = 0; i < 1000000; ++i)
{
my_map.insert(make_pair(i, 8));
}
clock_t beg = clock();
for(i = 0; i < 1000000; ++i)
returnPtr = my_map.insert(make_pair(1000001 + i, 8));
clock_t end = clock();
double time = (double)(end - beg)/CLOCKS_PER_SEC;
cout<<"insert(value)的運行時間: "<<time<<endl;
beg = clock();
map<int, int>::iterator ptr = returnPtr.first;
for(i = 0; i < 1000000; ++i)
ptr = my_map.insert(ptr, make_pair(1000001 + i, 8)); //每次都從上一次插入位置爲起點開始尋找插入位置
end = clock();
time = (double)(end - beg)/CLOCKS_PER_SEC;
cout<<"insert(iter, value)的運行時間: "<<time<<endl;
}
運行結果如下:
從運行結果可以看出,第三種指定查找起點的insert函數的效率要好很多。