c++ unordered_map 自定義key

C++11新增了一類散列容器包括unordered_set, unordered_map, unordered_multiset, unordered_multimap, 即以前熟悉的hash_set, hash_map等。html

這類容器底層以哈希表實現之,經過unordered_map介紹下這類容器的使用。ios

unordered_map 是一個模板類,須要咱們提供5個魔板參數。依次爲:key值的類型, value值的類型,hash函數, 等價函數, 容器分配器。其中後三個有默認參數,那咱們是否是隻須要提供前2個模板參數就能夠使用了呢? 不必定。當咱們使用的key爲內置類型時(如int, double, float, string等),後面三個默認模板參數在STL內有其特化版本,故能夠直接進行使用。可一旦你的類爲自定義類型, 其中的hash和equal就得由你本身提供。其實也不難理解, 假設你的對象是一塊石頭,石頭怎麼進行hash, 石頭怎麼怎麼比大小呢?編譯器固然不知道,這就須要你告訴編譯器。下面咱們對這2種狀況分別舉例說明。函數

(一)、當key爲內置類型:測試

unordered_map<string, int> m_map;

當key爲內置類型, 僅需提供key與value的類型即可運用。 其中hash<string> 與 equal <int> 均有特化版本,分配器對整個容器進行內存管理,這三個參數均爲默認參數。

(二)、當key爲自定義類型:spa

  好比咱們簡單定義一個package類,裏面僅有名字,電話2項數據。code

class package
{
public:
    string getName() const { return name; }
    long long getPhone() const { return phone; }

    package(string m_name = 0, long long m_pNum = 0);

    bool operator== (const package& p) const
    {    return name == p.name &&
               phone == p.phone; 
    }

private:
    string name;        
    long long phone;        
};

而後將原生hash包裝使用下:htm

namespace std
{
    template<>
    struct hash<package>
    {
        size_t operator() (const package& s) const noexcept
        {
            return  hash<decltype(s.getName())>()(s.getName()) +
                    hash<decltype(s.getPhone())>()(s.getPhone());
        }
    }; // 間接調用原生Hash.
}

或者能夠藉助藉助boost庫的hash_value:對象

namespace std
 {
     template<>
     struct hash<package>
     {
         size_t operator() (const package& s) const noexcept
         {
             auto t = make_tuple(s.getName(), s.getPhone());                                            
             size_t value = boost::hash_value(t);         
             return value;     // make_tuple(s.getName(), s.getPhone()) 等價於 tuple<string, long long>()(s.getName(), s.getPhone())
          }
     }; // 間接調用原生Hash.
  }

當咱們把Hash函數(package的特化版本)和 等價函數 (操做符==重載)提供後, 即可使用自定義版本的unordered_map了:blog

unordered_map<package, int> m_map;

下面給出測試代碼:內存

測試環境: VS2017

#include <iostream>
#include <unordered_map>
#include <string>
#include <algorithm>
//#include <boost/functional/hash.hpp>   // 根據安裝路徑選擇hash.hpp
#include <tuple>

using namespace std;


class package
{
public:
    string getName() const { return name; }
    long long getPhone() const { return phone; }

    package(string m_name = 0, long long m_pNum = 0);

    bool operator== (const package& p) const
    {    return name == p.name &&
               phone == p.phone; 
    }

private:
    string name;        
    long long phone;        
};

package::package(string m_name, long long m_pNum)
    : name(m_name), phone(m_pNum) { }

namespace std
{
    template<>
    struct hash<package>
    {
        size_t operator() (const package& s) const noexcept
        {
            return  hash<decltype(s.getName())>()(s.getName()) +
                    hash<decltype(s.getPhone())>()(s.getPhone());
            
            //auto t = make_tuple(s.getName(), s.getPhone());                                            
            //size_t value = boost::hash_value(t);         
            //return value;     // make_tuple(s.getName(), s.getPhone()) 等價於 tuple<string, long long>()(s.getName(), s.getPhone())
        }
    }; // 間接調用原生Hash.
}

int main()
{
    unordered_map<package, int> m_map;

    package p1{ "Wang", 13399996666};
    package p2{ "Li", 13399993333};
    package p3{ "Zhang", 13399992222};
    package p4{ "Zhou", 13399991111 };
    package p5{ "Wang", 13399996666};
    package p6{ "Wang", 13366669999 };

    m_map[p1]++;
    m_map[p2]++;
    m_map[p3]++;
    m_map[p4]++;
    m_map[p5]++;
    m_map[p6]++;

    cout << m_map.bucket(p1) << " ";
    cout << m_map.bucket(p2) << " ";
    cout << m_map.bucket(p3) << " ";
    cout << m_map.bucket(p4) << " ";
    cout << m_map.bucket(p5) << " ";
    cout << m_map.bucket(p6) << " " << endl;

    return 0;
}

本文轉自:https://www.cnblogs.com/xiguas/p/9977933.html

相關文章
相關標籤/搜索