STL hash_map使用

2023-11-03

 

今天在使用STL中的hash_map模板遇到使用PTCHAR作为Key时无法对字符串进行正确比较的问题,在网上查找相应的文章可惜没有找到,但找到了http://www.stlchina.org/twiki/bin/view.pl/Main/STLDetailHashMaphttp://www.cppblog.com/guojingjia2006/archive/2008/01/12/41037.aspx两篇文章对解决我的问题帮了大忙,特将其内容贴出。

 

hash_map类在头文件hash_map中,和所有其它的C++标准库一样,头文件没有扩展名。如下声明:

  

#include <hash_map>
using namespace std;
using namespace stdext;

     hash_map是一个聚合类,它继承自_Hash类,包括一个vector,一个list和一个pair,其中vector用于保存桶,list用于进行冲突处理,pair用于保存key->value结构,简要地伪码如下:

 class hash_map<class _Tkey, class _Tval>
{
private:
    typedef pair<_Tkey, _Tval> hash_pair;
    typedef list<hash_pair>    hash_list;
    typedef vector<hash_list>  hash_table;
};


     当然,这只是一个简单模型,C++标准库的泛型模版一向以嵌套复杂而闻名,初学时看类库,无疑天书啊。微软的hash_map类还聚合了hash_compare仿函数类,hash_compare类里又聚合了less仿函数类,乱七八糟的。

     下面说说使用方法:

     一、简单变量作为索引:整形、实性、指针型
     其实指针型也就是整形,算法一样。但是hash_map会对char*, const char*, wchar_t*, const wchar_t*做特殊处理。
     这种情况最简单,下面代码是整形示例:
 

hash_map<int, int> IntHash;
IntHash[1] = 123;
IntHash[2] = 456;
int val = IntHash[1];
int val = IntHash[2];

     实型和指针型用法和整形一样,原理如下:
     1、使用简单类型作索引声明hash_map的时候,不需要声明模版的后两个参数(最后一个参数指名hash_map节点的存储方式,默认为pair,我觉得这就挺好,没必要修改),使用默认值就好。
     2、对于除过字符串的其它简单类型,hash_map使用模版函数 size_t hash_value(const _Kty& _Keyval) 计算hash值,计算方法是经典的掩码异或法,自动溢出得到索引hash值。微软的工程师也许开了一个玩笑,这个掩码被定义为0xdeadbeef(死牛肉,抑或是某个程序员的外号)。
     3、对于字符串指针作索引的时候,使用定类型函数inline size_t hash_value(const char *_Str)或inline size_t hash_value(const wchar_t *_Str)计算hash值,计算方法是取出每一个字符求和,自动溢出得到hash值。对于字符串型的hash索引,要注意需要自定义less仿函数。
     因为我们有理由认为,人们使用hash表进行快速查找的预期成本要比在hash表中插入的预期成本低得多,所以插入可以比查找昂贵些;基于这个假设,hash_map在有冲突时,插入链表是进行排序插入的,这样在进行查询冲突解决的时候就能够更快捷的找到需要的索引。
     但是,基于泛型编程的原则,hash_map也有理由认为每一种类型都支持使用"<"来判别两个类型值的大小,这种设计恰好让字符串类型无所适从,众所周知,两个字符串指针的大小并不代表字符串值的大小。见如下代码:

hash_map<const char*, int> CharHash;
CharHash["a"] = 123;
CharHash["b"] = 456;
char szInput[64] = "";
scanf("%s", szInput);
int val = CharHash[szInput];

     最终的结果就是无论输入任何字符串,都无法找到对应的整数值。因为输入的字符串指针是szInput指针,和"a"或"b"字符串常量指针的大小是绝对不会相同。解决方法如下:
     首先写一个仿函数CharLess,继承自仿函数基类binary_function(当然也可以不继承,这样写只是符合标准,而且写起来比较方便,不用被类似于指针的指针和指针的引用搞晕。

          

struct CharLess : public binary_function<const char*, const char*, bool>
{
public:
    result_type operator()(const first_argument_type& _Left, const second_argument_type& _Right) const
    {
        return(stricmp(_Left, _Right) < 0 ? true : false);
    }
};

     很好,有了这个仿函数,就可以正确的使用字符串指针型hash_map了。如下:

         

hash_map<const char*, int, hash_compare<const char*, CharLess> > CharHash;
CharHash["a"] = 123;
CharHash["b"] = 456;
char szInput[64] = "";
scanf("%s", szInput);
int val = CharHash[szInput];

      
     现在就可以正常工作了。至此,简单类型的使用方法介绍完毕。

     二、用户自定义类型:比如对象类型,结构体。
     这种情况比价复杂,我们先说简单的,对于C++标准库的string类。
     
     庆幸的是,微软为basic_string(string类的基类)提供了hash方法,这使得使用string对象做索引简单了许多。值得注意(也值得郁闷)的是,虽然支持string的hash,string类却没有重载比较运算符,所以标准的hash_compare仿函数依旧无法工作。我们继续重写less仿函数。
         
         

 struct string_less : public binary_function<const string, const string, bool>
{ 
public: 
    result_type operator()(const first_argument_type& _Left, const second_argument_type& _Right) const 
    { 
        return(_Left.compare(_Right) < 0 ? true : fase); 
    } 
};
            
     好了,我们可以书写如下代码:
           
          
hash_map<string, int, hash_compare<string, string_less> > StringHash;
StringHash["a"] = 123;
StringHash["b"] = 456;
string strKey = "a";
int val = CharHash[strKey];

      
     这样就可以了。
     
     对于另外的一个常用的字符串类CString(我认为微软的CString比标准库的string设计要洒脱一些)更加复杂一些。很显然,标准库里不包含对于CString的支持,但CString却重载了比较运算符(郁闷)。我们必须重写hash_compare仿函数。值得一提的是,在Virtual Stdio 2003中,CString不再是MFC的成员,而成为ATL的成员,使用#include <atlstr.h>就可以使用。我没有采用重写hash_compare仿函数的策略,而仅仅是继承了它,在模版库中的继承是没有性能损耗的,而且能让我偷一点懒。
     首先重写一个hash_value函数:
     
        

inline size_t CString_hash_value(const CString& str) 
{ 
    size_t value = _HASH_SEED; 
    size_t size  = str.GetLength(); 
    if (size > 0) { 
        size_t temp = (size / 16) + 1; 
        size -= temp; 
        for (size_t idx = 0; idx <= size; idx += temp) { 
            value += (size_t)str[(int)idx]; 
        } 
    } 
    return(value); 
}


     
     其次重写hash_compare仿函数:
     
         
class CString_hash_compare : public hash_compare<CString> 
{ 
public: 
    size_t operator()(const CString& _Key) const 
    { 
        return((size_t)CString_hash_value(_Key));
    }
   
    bool operator()(const CString& _Keyval1, const CString& _Keyval2) const 
    { 
        return (comp(_Keyval1, _Keyval2)); 
    } 
};


           
     上面的重载忽略了基类对于less仿函数的引入,因为CString具备比较运算符,我们可以使用默认的less仿函数,在这里映射为comp。好了,我们可以声明新的hash_map对象如下:

          

hash_map<CString, int, CString_hash_compare> CStringHash;

     其余的操作一样一样的。

     下来就说说对于自定义对象的使用方法:首先定义
     
         

 struct IHashable 
{ 
    virtual unsigned long hash_value() const = 0; 
    virtual bool operator < (const IHashable& val) const = 0; 
    virtual IHashable& operator = (const IHashable& val) = 0; 
};


     
     让我们自写的类都派生自这里,有一个标准,接下来定义我们的类:
     
        
class CTest : public IHashable 
{ 
public: 
    int m_value; 
    CString m_message; 
public: 
    CTest() : m_value(0) {}
            
    CTest(const CTest& obj) 
    { 
        m_value = obj.m_value; 
        m_message = obj.m_message; 
    } 
public: 
    virtual IHashable& operator = (const IHashable& val) { 
        m_value   = ((CTest&)val).m_value; 
        m_message = ((CTest&)val).m_message; 
        return(*this); 
    }
            
    virtual unsigned long hash_value() const {
        // 这里使用类中的m_value域计算hash值,也可以使用更复杂的函数计算所有域总的hash值
        return(m_value ^ 0xdeadbeef  
    }
            
    virtual bool operator < (const IHashable& val) const { 
        return(m_value < ((CTest&)val).m_value); 
    } 
};

      
     用这个类的对象做为hash索引准备工作如下,因为接口中规定了比较运算符,所以这里可以使用标准的less仿函数,所以这里忽略:
     
         
template<class _Tkey> 
class MyHashCompare : public hash_compare<_Tkey> 
{ 
public: 
    size_t operator()(const _Tkey& _Key) const { 
        return(_Key.hash_value()); 
    }


    bool operator()(const _Tkey& _Keyval1, const _Tkey& _Keyval2) const { 
        return (comp(_Keyval1, _Keyval2)); 
    } 
};
            


     下来就这样写:
     
CTest test; 
test.m_value = 123; 
test.m_message = "This is a test";


MyHash[test] = 2005;


int val = MyHash[test];

     
     可以看到正确的数字被返回。
     
     三、关于hash_map的思考:
     
     1、性能分析:采用了内联代码和模版技术的hash_map在效率上应该是非常优秀的,但我们还需要注意如下几点:
     
     * 经过查看代码,字符串索引会比简单类型索引速度慢,自定义类型索引的性能则和我们选择hash的内容有很大关系,简单为主,这是使用hash_map的基本原则。
     * 可以通过重写hash_compair仿函数,更改里面关于桶数量的定义,如果取值合适,也可以得到更优的性能。如果桶数量大于10,则牢记它应该是一个质数。
     * 在自定义类型是,重载的等号(或者拷贝构造)有可能成为性能瓶颈,使用对象指针最为索引将是一个好的想法,但这就必须重写less仿函数,理由同使用字符串指针作为索引。

 

自己使用上面的方法成功解决了使用PTCHAR作为Key的使用,其解决方法如下:

 

inline size_t PTCHAR_hash_value(const PTCHAR str)
{
    size_t value = _HASH_SEED;
    size_t size = _tcslen(str);
    if (size > 0) {
        size_t temp = (size/16) + 1;
        size -= temp;
        for (size_t idx=0; idx<=size; idx+=temp) {
            value += (size_t)str[(int)idx];
        }
    }
    return value;
}
class PTCHAR_hash_compare : public stdext::hash_compare<PTCHAR>
{
public:
    size_t operator()(const PTCHAR _Key) const {
        return ((size_t)PTCHAR_hash_value(_Key));
    }
    bool operator()(const PTCHAR _Keyval1, const PTCHAR _Keyval2) const {
        return (_tcscmp(_Keyval1, _Keyval2));
    }
};
stdext::hash_map<PTCHAR, long, PTCHAR_hash_compare > myHash;


 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STL hash_map使用 的相关文章

  • 构造函数中显式关键字的使用

    我试图了解 C 中显式关键字的用法 并查看了这个问题C 中的explicit关键字是什么意思 https stackoverflow com questions 121162 但是 那里列出的示例 实际上是前两个答案 对于用法并不是很清楚
  • 匿名结构体作为返回类型

    下面的代码编译得很好VC 19 00 23506 http rextester com GMUP11493 标志 Wall WX Za 与VC 19 10 25109 0 标志 Wall WX Za permissive 这可以在以下位置检
  • 如何从 JavaScript 调用 ASSX?

    我想调用一个 ASHX 文件并从 JavaScript 传递一些查询字符串变量 并将返回字符串获取到 JavaScript 中的字符串中 我该怎么做 ASHX 文件已被编码为response write 一个基于查询字符串的字符串 像这样的
  • 递归检查字符串中的所有字母是否都是大写

    我必须检查递归中所有字母是否都是大写字母 我不知道为什么这不起作用 public static bool IsCapital string str if str Length 1 return int Parse str 0 ToStrin
  • 多个对象以某种方式相互干扰[原始版本]

    我有一个神经网络 NN 当应用于单个数据集时 它可以完美地工作 但是 如果我想在一组数据上运行神经网络 然后创建一个新的神经网络实例以在不同的数据集 甚至再次同一组数据 上运行 那么新实例将产生完全错误的预测 例如 对 XOR 模式进行训练
  • 根据列中的部分字符串匹配选择数据框行

    我想根据列中字符串的部分匹配从数据框中选择行 例如列 x 包含字符串 hsa 使用sqldf if它有一个like语法 我会做类似的事情 select from lt gt where x like hsa 很遗憾 sqldf不支持该语法
  • memcpy 到动态存储结构安全吗?

    Context 我正在审查一些代码 这些代码从 IO 描述符接收数据到字符缓冲区 对其进行一些控制 然后使用接收到的缓冲区的一部分来填充结构 突然想知道是否可能涉及严格的别名规则违规 这是一个简化版本 define BFSZ 1024 st
  • Python 中的密码子生成

    我有这段代码 用于将 DNA 字符串转换为密码子列表 然后将此列表转换为具有各自氨基酸的字符串 然而 当我运行代码并且 DNA 字符串以一对核苷酸 例如 CT 而不是三联体结尾时 代码不会生成氨基酸序列 正如您在输出中看到的 from co
  • 字节到二进制字符串 C# - 显示所有 8 位数字

    我想在文本框中显示一个字节 现在我正在使用 Convert ToString MyVeryOwnByte 2 但是 当字节开头有 0 时 这些 0 就会被删除 例子 MyVeryOwnByte 00001110 Texbox shows g
  • 比 BMH (Boyer–Moore–Horspool) 更快的算法

    您会使用哪种算法来搜索短文本中的短子字符串 简而言之 我的意思是子字符串有 5 10 个字符 字符串有 255 个字符 我正在考虑根据输入数据长度选择算法 哪种算法对于较长的输入更好 Try Turbo BM http www igm un
  • Swift 结构类型集

    说我有一个struct 可以是任何东西 struct Cube var x Int var y Int var z Int var width Int 然后我该如何创建一个Set这些点中 是否存在两个具有相同属性的对象 let points
  • 正则表达式在 R 中同时多次包含字母/特殊字符时删除单词

    我想删除那些单词中字母 特殊字符的数量同时出现两次以上的单词 例如 输入就像 Google in theee lland of whhhat c c and e 输出应该是 Google in lland of c c and x lt G
  • Matlab strcat 不返回字符串?

    imgstr 无法识别 strcat 的输出字符串 homedir C Users images for img 01 bmp 02 bmp 03 bmp imgstr strcat homedir img I imread imgstr
  • 如何在Java中对对象数组进行字段级别排序以进行等级比较?

    In Java Class StudentProgress String Name String Grade CTOR goes here main class main method StudentProgress arrayofObje
  • Qt:删除富文本

    对于明文有QFontMetrics elideText https doc qt io qt 5 qfontmetrics html elidedText https doc qt io qt 5 qfontmetrics html eli
  • 将变量从 JSON 文件加载到 LESS CSS 预处理器中

    是否可以像使用 Stylus 一样将变量从 JSON 文件加载到 LESS CSS 预处理器中 与文件内容myvars json color1 112345 color2 667890 在 Stylus 中我 json myvars jso
  • TypeScript - 如何从方法的参数推断类泛型类型?

    我正在尝试从稍后调用的方法参数中输入类泛型 在我们调用带有泛型参数的方法之前 类的泛型类型是不知道的 然后 对于任何其他方法 将传递泛型类型 老实说 对我来说 这似乎是一个非常复杂的功能 我什至不确定 TypeScript 是否有办法做到这
  • 在静态类中存储连接 (ASP.NET)

    由于我使用的是 Postgresql 并且无法使用 LINQ to SQL 因此我编写了自己的包装器类 这是学生课程的一部分 public class Student User private static NpgsqlConnection
  • Java byte[] 与 String 之间的转换

    为什么这个junit测试失败了 import org junit Assert import org junit Test import java io UnsupportedEncodingException public class T
  • 错误:“字符串”无法转换为“字符串!”

    mapView rac valuesForKeyPath userTrackingMode observer self subscribeNextAs block handling 我收到一个错误 String is not convert

随机推荐

  • 重新映射图像——OpenCV Remap实例

    重新映射图像 OpenCV Remap实例 在计算机视觉领域中 图像的几何变换是一项重要的工作 重要的任务之一是将图像转换为其他形式 例如投影或扭曲 OpenCV的Remap函数提供了一个简单和灵活的方法来执行这种类型的变换 下面展示了如何
  • unsigned char 数值溢出问题

    include
  • 在D盘使用SVN检出文件后,整个盘出现蓝色问号的解决办法。

    在D盘使用SVN检出文件后 整个盘出现蓝色问号的解决办法 原因 在该盘的根目录执行了checkout操作 SVN将整个盘作为了一个版本库的本地副本 那些问号表示这些文件没有被SVN控制 解决方法 1 在文件上右击 选择TortoiseSVN
  • android studio电影院选座,8排电影院选座最佳位置

    8排电影院选座最佳位置在哪里呢 8排电影院属于小影厅 小影厅银幕宽度在10米以下 座位100以内 座位排数通常拥有8 14排 小影厅整体空间小 选座时要选中间稍靠后一些的位置 由于整体排数少 因此选即便选择靠后一些的排数实际上距银幕的距离也
  • ubuntu 同时使用无线网卡和有线网卡

    转载于这位博主 文章
  • Ubuntu18.04 取消开机密码 实现自动登录

    因为要把Ubuntu设备作为服务器 实现开机自动运行服务程序 所以需要取消开机密码 实现自动登录 1 点击桌面右上角向下的箭头 点击设置图标 2 点击右上角的 Unlock 3 在弹出的窗口中输入系统登录密码 点击右下角 Authentic
  • OpenMP并行编程

    1 总览 OpenMP Open Multi Processing 是一种用于共享内存并行系统的多线程程序设计方案 支持的编程语言包括C C 和Fortran OpenMP提供了对并行算法的高层抽象描述 通过线程实现并行化 特别适合在多核C
  • springboot使用logback日志框架超详细教程

    前言 项目中日志系统是必不可少的 目前比较流行的日志框架有log4j logback等 可能大家还不知道 这两个框架的作者是同一个人 Logback旨在作为流行的log4j项目的后续版本 从而恢复log4j离开的位置 另外 slf4j Si
  • 阶乘约数

    include
  • 【4月第二周学习记录】数据结构与算法王卓-第六章图-图的遍历(邻接矩阵与邻接表,DFS与BFS)

    1 图的遍历基本思路与方法 图的遍历的定义与visited数组 常用的遍历方法 深度优先搜索遍历 Depth First Search DFS 广度优先搜索遍历 Breadth First Search BFS 2 深度优先搜索遍历 Dep
  • 华为SMC2.0视频会议系统总结(一)

    简单总结下 新上手的华为视频会议SMC2 0会控系统 第一次接触华为的会控系统 理解的不是很深刻 简单的记下来 省得以后忘记 因为客户使用的泛微OA系统 我们公司 南大智慧 负责提供华为设备 并做相应的接口开发工作 我们主要的工作内容就是确
  • 控制器的编码器

    一 原理 控制器内部为每个轴配置了脉冲计数装置 控制器默认的脉冲计数源是外部编码器 如果用户 在接线时将外部编码器的信号与端子板 25pin轴接口的编码器信号接在一起 就可以调用指令读取外部编码器的值 如果用户没有接外部编码器反馈信号 例如
  • java基础学习 day22(方法,return,重载)

    1 方法 是程序中最小的执行单元 方法里面的代码 要么全都执行 要么全都不执行 重复的代码 具有独立功能的代码可以抽取到方法中 方法的好处 可以提高代码的复用性 可以提高代码的可维护性 java虚拟机在运行时会先自动调用main 方法 2
  • ## 带AB相编码器直流减速电机测转动速度及角度深度解析

    带AB相编码器直流减速电机测转动速度及角度深度解析 下图为编码器输出的AB相波形 一般情况下 我们只测A相 或B相 的上升沿或下降沿 但四倍频的方法是测A相和B相的上升沿和下降沿 在同样的时间内 计数脉冲是以前的4倍 然后stm32单片机可
  • 一致性的3种协议,并发,事务

    Two Phase Commit MVCC Paxos TPC对应于传统数据库上的local cluster的一致性 分布式事务 每个节点上的local事务可以是不同的亦可以是相同的 replica MVCC的思想是抓住Transactio
  • vue项目中使用vee-validate表单验证

    一 写在前面 作为前端开发 在项目中避免不了做表单到页面 做表单页面就避免不了要做表单效验 如果多个表单页面有相同都表单比如用户名 密码等等 不能每个页面都写一次验证规则 作者项目平时使用都vue比较多 所有使用vee validate插件
  • C++标准模板库(Standard Template Library,STL)

    文章目录 标准模板库介绍 C 标准库头文件 STL 组成 迭代器 算法 适配器 标准模板库介绍 标准模板库 Standard Template Library STL 是惠普实验室开发的一系列软件的统称 虽说它主要出现到C 中 但在被引入C
  • JDK安装及JAVA环境变量配置(JDK1.8版本)

    一 JDK官网下载地址 https www oracle com technetwork java javase downloads jdk12 downloads 5295953 html JDK1 8下载地址 https www ora
  • 网络爬虫反反爬小技巧(二)Pyppeteer

    上一节说到了Selenium 它的功能的确非常强大 但很多时候我们会发现 Selenium 还是有一些不太方便的地方 比如速度太慢 对版本配置要求严苛 最麻烦是经常要更新对应的驱动 还有些网页是可以检测到是否使用了Selenium 所以在这
  • STL hash_map使用

    今天在使用STL中的hash map模板遇到使用PTCHAR作为Key时无法对字符串进行正确比较的问题 在网上查找相应的文章可惜没有找到 但找到了http www stlchina org twiki bin view pl Main ST