C++ std::unordered_map使用及如何自定義鍵的類型

C++ std::unordered_map使用及如何自定義鍵的類型

  • 頭文件:<unordered_map>

  • 定義:

      template<class _Key, class _Tp,
    	   class _Hash = hash<_Key>,
    	   class _Pred = std::equal_to<_Key>,
    	   class _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
        class unordered_map{...}
    
    _Hash = hash<_Key> 由於std::unordered_map 使用哈希算法,將值存儲到桶中,如果自定義鍵的類型,需要實現對應方法。如果使用基本類型則不用提供
    _Pred = std::equal_to<_Key> 由於hash算法可能會遇到hash值相同,但內容不同的情況,提供函數來區分
    _Alloc = std::allocator<std::pair<const _Key, _Tp> > >則是內存管理使用,一般來說使用默認的內存管理即可
    
  • 公共定義的類型

    /// Public typedefs.
    typedef typename _Hashtable::key_type	key_type;  //鍵類型
    typedef typename _Hashtable::value_type	value_type; // std::Pair<const _Key k, _Tp t>
    typedef typename _Hashtable::mapped_type	mapped_type;//值的類型
    typedef typename _Hashtable::hasher	hasher;
    typedef typename _Hashtable::key_equal	key_equal;
    typedef typename _Hashtable::allocator_type allocator_type;
    
  • 構造函數

    void testCreateMap(){
        using Group = std::unordered_map<int,std::string>;
        Group a;  // 默認構造函數
        Group b{{1,"12"},{2,"22"}}; // c++11 初始化列表構造
        Group c(b.begin(),b.end()); // 兩個迭代器
        Group d(std::move(b)); // 移動構造
        Group f(d); // 複製構造
        std::cout << "d size: " <<  d.size() << std::endl;
        for (const auto& item : d){
            std::cout << "key: "<< item.first << ",value: " << item.second << std::endl;
        }
    }
    結果:
    d size: 2
    key: 1,value: 12
    key: 2,value: 22
    
  • 插入、查找、遍歷

    void test_insert_find_traverse(){
        // 使用重載的[]進行插入,查找
        // 問題是在查找時,比如例子中13鍵不存在,map會使用string的默認構造函數插入,這時map有兩對pair {12,“sdf"},{13,""}
        using Group = std::unordered_map<int,std::string>;
        Group a;
        a[12] = "use []";
        std::cout << a[12] << std::endl;
        std::cout << a[13] << std::endl;
        std::cout << a.size() << std::endl;
        std:: cout << "insert" << std::endl;
        // 使用insert 或emplace
        // insert 與 emplace 結果相同,返回的是std::pair<iterator, bool>,iterator是插入位置的迭代器,bool爲是否從插入成功
        // iterator 的鍵爲const類型 值可修改
        Group b{{12,"init"}};
        auto insert_result = b.insert(std::make_pair(12,"insert"));
        // 查看鍵值 由於 當前鍵已使用,插入失敗
        std::cout << "if successful insert: " <<  (bool)insert_result.second
        << ", insert place: " << (*insert_result.first).first << ",value: " <<  (*insert_result.first).second << std::endl;
        // 修改值
        (*insert_result.first).second = "changed";
        // emplace 與insert功能相同,只是參數不用使用std::make_pair
        auto emplace_result = b.emplace(14,"emplace");
        for (const auto& item : b){
            std::cout << item.first  << " " << item.second << std::endl;
        }
        // 查找值
        //可以使用find(key)進行查找
        auto find_res = b.find(14);
        if (find_res != b.end()){
            // 查找成功
            std::cout << "found\n";
        } else{
            std::cout << "not found\n";
        }    
        // 遍歷 希望遍歷時修改值的值,可以使用這種方法,平時可以使用前面的方法
        for (auto item = b.begin();item != b.end();++item){
            item->second += "vvvvv";
            std::cout << "key: " << item->first << ", value: " << item->second << std::endl;
        }
    }
    結果
    use []
    
    2
    insert
    if successful insert: 0, insert place: 12,value: init
    14 emplace
    12 changed
    found
    key: 14, value: emplacevvvvv
    key: 12, value: changedvvvvv
    
  • 可以寫一個模板函數來跳過[]查找引發的問題

    template <class Map>
    typename Map::mapped_type inline get_default(
            const Map& _map,
            const typename Map::key_type& key,
            const typename Map::mapped_type& dlft = typename Map::mapped_type()
            )
    {
        auto pos = _map.find(key);
        return (pos != _map.end() ? pos->second : dlft);
    }
    使用
    auto p = get_default(g,12);  
    auto p = get_default(g,12,"default");
    
  • 一個使用自定義鍵的例子

    class Position{
    private:
        int m_x = 0;
        int m_y = 0;
    public:
        int x() const { return m_x;}
        int y() const { return m_y;}
        Position(int _x,int _y) : m_x(_x),m_y(_y) {};
        // equal_to
        bool operator==(const Position& op) const { return m_x == op.m_x && m_y == op.m_y;}
    };
    
    // 定義hash函數
    template <class T> inline void hash_combine(std::size_t &seed, const T &v){
        std::hash<T> hasher;
        seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    }
    // Position特定模板函數
    namespace std{
        template <> struct hash<Position>{
            size_t operator()(const Position& p) const{
                auto key = hash<int>()(p.x());
                hash_combine(key,p.y());
                return key;
            }
        };
    }
    class Person{
    private:
        int m_age;
        std::string m_name;
    public:
        Person():m_age(0),m_name(){}
        Person(int _age,std::string _name) : m_age(_age),m_name(std::move(_name)){};
        int age() const { return m_age;}
        std::string name() const { return m_name;}
        bool empty(){ return m_age == 0 && m_name.empty();}
        void print(){std::cout << "age: " << m_age << ", name: " << m_name << std::endl; }
    };
    template <class Map>
    typename Map::mapped_type inline get_default(
            const Map& _map,
            const typename Map::key_type& key,
            const typename Map::mapped_type& dlft = typename Map::mapped_type()
            )
    {
        auto pos = _map.find(key);
        return (pos != _map.end() ? pos->second : dlft);
    }
    void testUnorderedMap(){
        using Group = std::unordered_map<Position,Person>;
        Group a{{Position(1,2),Person(12,"adsf")}};
        auto result = a.insert(std::make_pair(Position(22,2),Person(22,"adddsf")));
        Group g(a.begin(),a.end());
        auto p = get_default(g,Position(1,2));
        if (p.empty()){
            std::cout << "not found" << std::endl;
        } else {
            p.print();
        }
    }
    int main(){
        testUnorderedMap();
        return 0;
    }
    結果
    age: 12, name: adsf
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章