zoukankan      html  css  js  c++  java
  • 关联容器: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<<"
    
    ";
        /****************修改*****************/
        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.
    ";
    
        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 << "
    ";
        }
    
        cout <<"
    key:'apple' is in bucket #" << mymap.bucket("apple") <<endl;
        cout <<"
    key:'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常用的功能函数介绍就这么多了,还有一些比较不常用的功能的介绍,可以参考这里

  • 相关阅读:
    GitHub统计
    不错的第三方控件
    仿射变换(CGAffineTransform)使用小结
    AffineTransform(仿射变换)
    使用CAShapeLayer实现复杂的View的遮罩效果
    使用CAShapeLayer实现一个音量大小动态改变的控件
    window10 Docker仓库访问
    postgresql从timestamp(6)复制到timestamp(0),时间会变
    在编译Dll文件的时候遇到dll 链接不一致的问题
    qtquery 取列的值
  • 原文地址:https://www.cnblogs.com/voidsky/p/5373929.html
Copyright © 2011-2022 走看看