Tsai笔记:C++学习随性笔记(6)—— hashmap (现在已包含在STL中unordered_map)

Tsai笔记:C++学习随性笔记(6)—— hashmap

一.介绍

1.1 特性

  1. 关联性:通过key去检索value,而不是通过绝对地址(和顺序容器不同)
  2. 无序性:使用hash表存储,内部无序
  3. Map : 每个值对应一个键值
  4. 键唯一性:不存在两个元素的键一样
  5. 动态内存管理:使用内存管理模型来动态管理所需要的内存空间

1.2 Hashtable和bucket

        由于unordered_map内部采用的hashtable的数据结构存储,所以,每个特定的key会通过一些特定的哈希运算映射到一个特定的位置,我们知道,hashtable是可能存在冲突的(多个key通过计算映射到同一个位置),在同一个位置的元素会按顺序链在后面。所以把这个位置称为一个bucket是十分形象的(像桶子一样,可以装多个元素)。

       所以unordered_map内部其实是由很多哈希桶组成的,每个哈希桶中可能没有元素,也可能有多个元素。

二. 模版

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.1 构造函数

unordered_map的构造方式有几种:

  1. 构造空的容器
  2. 复制构造
  3. 范围构造
  4. 用数组构造

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:" << endl;
    for (auto& x: sixth) 
        cout << x.first << " : " << x.second << endl;

    return 0;
}

输出结果:

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

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
coffee: 10
sugar: 0.8
baking powder: 0.3
eggs: 6
flour: 1.5
milk: 2

found coffee is 10

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

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

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值