1. 程式人生 > >關聯容器:unordered_map詳細介紹(附可執行程式碼)

關聯容器:unordered_map詳細介紹(附可執行程式碼)

1.介紹

最近使用到一個c++的容器——unordered_map,它是一個關聯容器,內部採用的是hash表結構,擁有快速檢索的功能。

1.1 特性

  1. 關聯性:通過key去檢索value,而不是通過絕對地址(和順序容器不同)
  2. 無序性:使用hash表儲存,內部無序
  3. Map : 每個值對應一個鍵值
  4. 鍵唯一性:不存在兩個元素的鍵一樣
  5. 動態記憶體管理:使用記憶體管理模型來動態管理所需要的記憶體空間

1.2 Hashtable和bucket

由於unordered_map內部採用的hashtable的資料結構儲存,所以,每個特定的key會通過一些特定的雜湊運算對映到一個特定的位置,我們知道,hashtable是可能存在衝突的(多個key通過計算對映到同一個位置),在同一個位置的元素會按順序鏈在後面。所以把這個位置稱為一個bucket是十分形象的(像桶子一樣,可以裝多個元素)。可以參考

這篇介紹雜湊表的文章

這裡寫圖片描述

所以unordered_map內部其實是由很多雜湊桶組成的,每個雜湊桶中可能沒有元素,也可能有多個元素。

2. 模版

template < class Key,                                    // unordered_map::key_type
           class T,                                      // unordered_map::mapped_type
           class Hash = hash<Key>,                       // unordered_map::hasher
class Pred = equal_to<Key>, // unordered_map::key_equal class Alloc = allocator< pair<const Key,T> > // unordered_map::allocator_type > class unordered_map;

主要使用的也是模板的前2個引數<鍵,值>(需要更多的介紹可以點選這裡

unordered_map<const Key, T>
map;

2.1 迭代器

unordered_map的迭代器是一個指標,指向這個元素,通過迭代器來取得它的值。

unordered_map<Key,T>::iterator it;
(*it).first;             // the key value (of type Key)
(*it).second;            // the mapped value (of type T)
(*it);                   // the "element value" (of type pair<const Key,T>) 

它的鍵值分別是迭代器的first和second屬性。

it->first;               // same as (*it).first   (the key value)
it->second;              // same as (*it).second  (the mapped value) 

3. 功能函式

3.1 建構函式

unordered_map的構造方式有幾種:
- 構造空的容器
- 複製構造
- 範圍構造
- 用陣列構造

3.1.2示例程式碼

// constructing unordered_maps
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;

typedef unordered_map<string,string> stringmap;

stringmap merge (stringmap a,stringmap b) {
  stringmap temp(a); temp.insert(b.begin(),b.end()); return temp;
}

int main ()
{
  stringmap first;                              // 空
  stringmap second ( {{"apple","red"},{"lemon","yellow"}} );       // 用陣列初始
  stringmap third ( {{"orange","orange"},{"strawberry","red"}} );  // 用陣列初始
  stringmap fourth (second);                    // 複製初始化
  stringmap fifth (merge(third,fourth));        // 移動初始化
  stringmap sixth (fifth.begin(),fifth.end());  // 範圍初始化

  cout << "sixth contains:";
  for (auto& x: sixth) cout << " " << x.first << ":" << x.second;
  cout << endl;

  return 0;
}

輸出結果:

sixth contains: apple:red lemon:yellow orange:orange strawberry:red

3.2 容量操作

3.2.1 size

size_type size() const noexcept;

返回unordered_map的大小

3.2.2 empty

bool empty() const noexcept;

- 為空返回true
- 不為空返回false,和用size() == 0判斷一樣。

3.3 元素操作

3.3.1 find

iterator find ( const key_type& k );

查詢key所在的元素。
- 找到:返回元素的迭代器。通過迭代器的second屬性獲取值
- 沒找到:返回unordered_map::end

3.3.2 insert

插入有幾種方式:
- 複製插入(複製一個已有的pair的內容)
- 陣列插入(直接插入一個二維陣列)
- 範圍插入(複製一個起始迭代器和終止迭代器中間的內容)
- 陣列訪問模式插入(和陣列的[]操作很相似)

具體的例子可以看後面示例程式碼。

3.3.3 at

mapped_type& at ( const key_type& k );

查詢key所對應的值
- 如果存在:返回key對應的值,可以直接修改,和[]操作一樣。
- 如果不存在:丟擲 out_of_range 異常.

mymap.at(“Mars”) = 3396; //mymap[“Mars”] = 3396

3.3.4 erase

擦除元素也有幾種方式:

  • 通過位置(迭代器)

    iterator erase ( const_iterator position );
    
  • 通過key

    size_type erase ( const key_type& k );
    
  • 通過範圍(兩個迭代器)

    iterator erase ( const_iterator first, const_iterator last );
    

3.3.5 clear

void clear() noexcept

清空unordered_map

3.3.6 swap

void swap ( unordered_map& ump );

交換兩個unordered_map(注意,不是交換特定元素,是整個交換兩個map中的所有元素)

3.3.7 示例程式碼

// unordered_map::insert
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;

void display(unordered_map<string,double> myrecipe,string str)
{
    cout << str << endl;
    for (auto& x: myrecipe)
        cout << x.first << ": " << x.second << endl;
    cout << endl;
}

int main ()
{
    unordered_map<string,double>
    myrecipe,
    mypantry = {{"milk",2.0},{"flour",1.5}};

    /****************插入*****************/
    pair<string,double> myshopping ("baking powder",0.3);
    myrecipe.insert (myshopping);                        // 複製插入
    myrecipe.insert (make_pair<string,double>("eggs",6.0)); // 移動插入
    myrecipe.insert (mypantry.begin(), mypantry.end());  // 範圍插入
    myrecipe.insert ({{"sugar",0.8},{"salt",0.1}});    // 初始化陣列插入(可以用二維一次插入多個元素,也可以用一維插入一個元素)
    myrecipe["coffee"] = 10.0;  //陣列形式插入

    display(myrecipe,"myrecipe contains:");

    /****************查詢*****************/
    unordered_map<string,double>::const_iterator got = myrecipe.find ("coffee");

    if ( got == myrecipe.end() )
        cout << "not found";
    else
        cout << "found "<<got->first << " is " << got->second<<"\n\n";
    /****************修改*****************/
    myrecipe.at("coffee") = 9.0;
    myrecipe["milk"] = 3.0;
    display(myrecipe,"After modify myrecipe contains:");


    /****************擦除*****************/
    myrecipe.erase(myrecipe.begin());  //通過位置
    myrecipe.erase("milk");    //通過key
    display(myrecipe,"After erase myrecipe contains:");

    /****************交換*****************/
    myrecipe.swap(mypantry);
    display(myrecipe,"After swap with mypantry, myrecipe contains:");

    /****************清空*****************/
    myrecipe.clear();
    display(myrecipe,"After clear, myrecipe contains:");
    return 0;
}

輸出結果:

myrecipe contains:
salt: 0.1
milk: 2
flour: 1.5
coffee: 10
eggs: 6
sugar: 0.8
baking powder: 0.3

found coffee is 10

After modify myrecipe contains:
salt: 0.1
milk: 3
flour: 1.5
coffee: 9
eggs: 6
sugar: 0.8
baking powder: 0.3

After erase myrecipe contains:
flour: 1.5
coffee: 9
eggs: 6
sugar: 0.8
baking powder: 0.3

After swap with mypantry, myrecipe contains:
flour: 1.5
milk: 2

After clear, myrecipe contains:

3.4 迭代器和bucket操作

3.4.1 begin

  iterator begin() noexcept;
  local_iterator begin ( size_type n );
  • begin() : 返回開始的迭代器(和你的輸入順序沒關係,因為它的無序的)
  • begin(int n) : 返回n號bucket的第一個迭代器

3.4.2 end

  iterator end() noexcept;
  local_iterator end( size_type n );
  • end(): 返回結束位置的迭代器
  • end(int n) : 返回n號bucket的最後一個迭代器

3.4.3 bucket

size_type bucket ( const key_type& k ) const;

返回通過雜湊計算key所在的bucket(注意:這裡僅僅做雜湊計算確定bucket,並不保證key一定存在bucket中!)

3.4.4 bucket_count

size_type bucket_count() const noexcept;

返回bucket的總數

3.4.5 bucket_size

size_type bucket_size ( size_type n ) const;

返回第i個bucket的大小(這個位置的桶子裡有幾個元素,注意:函式不會判斷n是否在count範圍內)

3.4.6 示例程式碼

// unordered_map::bucket_count
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;

int main ()
{
    unordered_map<string,string> mymap =
    {
        {"house","maison"},
        {"apple","pomme"},
        {"tree","arbre"},
        {"book","livre"},
        {"door","porte"},
        {"grapefruit","pamplemousse"}
    };
    /************begin和end迭代器***************/
    cout << "mymap contains:";
    for ( auto it = mymap.begin(); it != mymap.end(); ++it )
        cout << " " << it->first << ":" << it->second;
    cout << endl;
    /************bucket操作***************/
     unsigned n = mymap.bucket_count();

    cout << "mymap has " << n << " buckets.\n";

    for (unsigned i=0; i<n; ++i)
    {
        cout << "bucket #" << i << "'s size:"<<mymap.bucket_size(i)<<" contains: ";
        for (auto it = mymap.begin(i); it!=mymap.end(i); ++it)
            cout << "[" << it->first << ":" << it->second << "] ";
        cout << "\n";
    }

    cout <<"\nkey:'apple' is in bucket #" << mymap.bucket("apple") <<endl;
    cout <<"\nkey:'computer' is in bucket #" << mymap.bucket("computer") <<endl;

    return 0;
}

輸出結果:

mymap contains: door:porte grapefruit:pamplemousse tree:arbre apple:pomme book:livre house:maison
mymap has 7 buckets.
bucket #0's size:2 contains: [book:livre] [house:maison]
bucket #1's size:0 contains:
bucket #2's size:0 contains:
bucket #3's size:2 contains: [grapefruit:pamplemousse] [tree:arbre]
bucket #4's size:0 contains:
bucket #5's size:1 contains: [apple:pomme]
bucket #6's size:1 contains: [door:porte]

key:'apple' is in bucket #5

key:'computer' is in bucket #6

最後

unordered_map常用的功能函式介紹就這麼多了,還有一些比較不常用的功能的介紹,可以參考這裡