Qt扫盲-QHash理论总结

news2024/9/20 15:07:05

QHash理论总结

  • 一、概述
    • 二、使用
    • 1. 添加 元素
    • 2. 获取元素
    • 3. 遍历元素
    • 4. 删除元素
    • 5. qHash()的散列函数
    • 6.算法复杂性

一、概述

QHash是Qt的通用容器类之一。它和QMap一样是用来存储(键,值)对的工具,并提供快速查找与键相关联的值的功能。
QHash提供了与QMap非常相似的功能。区别在于:

  • QHash提供了比QMap更快的查找。
  • 在遍历QMap时,元素总是按键排序。在QHash时,元素在QHash内部的顺序是无序的QMap内部是有序的
  • QMap的键类型必须含有< ()运算符。QHash的键类型必须能提供==()运算符和一个名为QHash()的全局散列函数(参见QHash)。其实就是构造函数要重载这些运算符,一般我们用的都是QString做键,所以不要慌。

在这里插入图片描述

二、使用

1. 添加 元素

下面是一个QHash的例子,键为QString,值为int:

  QHash<QString, int> hash;

要在散列中插入一个(键,值)对,可以使用运算符:

  hash["one"] = 1;
  hash["three"] = 3;
  hash["seven"] = 7;

这会将以下3个(键,值)对插入到QHash中:(“one”,1), (“three”,3),和(“seven”,7)。
另一种向散列中插入元素的方法是使用insert():

  hash.insert("twelve", 12);

要查找一个值,可以使用operator或value():

  int num1 = hash["thirteen"];
  int num2 = hash.value("thirteen");

如果散列中没有指定键的项,这些函数返回一个默认构造的值。int、double类返回 0,QString 返回空字符串。

2. 获取元素

如果你想检查散列值是否包含特定的键,可以使用contains():

  int timeout = 30;
  if (hash.contains("TIMEOUT"))
      timeout = hash.value("TIMEOUT");

还有一个value()重载方法,如果指定的键不存在,则使用第二个参数作为默认值:

int timeout = hash.value("TIMEOUT", 30);

一般来说,我们推荐使用contains()和value()而不是 [ ] 在散列中查找键。
原因在于,如果没有键相同的元素存在,[ ]运算符会静默地将元素插入到散列中(除非散列值是const)。
例如,下面的代码片段将在内存中创建1000个元素:

  // WRONG
  QHash<int, QWidget *> hash;
  ...
  for (int i = 0; i < 1000; ++i) {
      if (hash[i] == okButton)
          cout << "Found button at index " << i << Qt::endl;
  }

为了避免这个问题,请将上面代码中的hash[i]替换为hash.value(i)。
在内部,QHash使用散列表来执行查找。这个散列表会自动增长和缩小,以提供快速查找,而不会浪费太多内存。如果你已经知道QHash大约包含多少个元素,那么仍然可以通过调用reserve()来控制散列表的大小,但这对获得良好的性能来说不是必要的。你也可以调用capacity()来取得散列表的大小。

3. 遍历元素

如果想遍历存储在QHash中的所有键值对,可以使用迭代器。QHash提供了java风格的迭代器(QHashIterator和QMutableHashIterator)和stl风格的迭代器(QHash::const_iterator和QHash::iterator)。下面是如何使用java风格的迭代器迭代QHash<QString, int>:

  QHashIterator<QString, int> i(hash);
  while (i.hasNext()) {
      i.next();
      cout << i.key() << ": " << i.value() << Qt::endl;
  }

下面是相同的代码,但使用了stl风格的迭代器:

  QHash<QString, int>::const_iterator i = hash.constBegin();
  while (i != hash.constEnd()) {
      cout << i.key() << ": " << i.value() << Qt::endl;
      ++i;
  }

QHash是无序的,因此不能假定迭代器的序列是可预测的。如果需要按键排序,则使用QMap。
通常,QHash每个键只允许一个值。如果使用QHash中已经存在的键调用insert(),前一个值将被删除。例如:

  hash.insert("plenty", 100);
  hash.insert("plenty", 2000);
  // hash.value("plenty") == 2000

然而,你可以使用insertMulti()而不是insert()来为每个键存储多个值(或者使用便捷的子类QMultiHash)。

如果想取得一个键对应的所有值,可以使用values(const key &key),它会返回一个QList:

  QList<int> values = hash.values("plenty");
  for (int i = 0; i < values.size(); ++i)
      cout << values.at(i) << Qt::endl;

共享相同键的项获取的方法是调用find()来获取第一个键对应的元素的迭代器,然后从那里开始迭代:

  QHash<QString, int>::iterator i = hash.find("plenty");
  while (i != hash.end() && i.key() == "plenty") {
      cout << i.value() << Qt::endl;
      ++i;
  }

如果你只需要从散列中提取值(而不是键),也可以使用foreach:

  QHash<QString, int> hash;
  ...
  foreach (int value, hash)
      cout << value << Qt::endl;

4. 删除元素

有几种方法可以从散列中删除元素。一种方法是调用remove()方法;这将删除具有给定键的任何项。另一种方法是使用QMutableHashIterator::remove()。此外,还可以使用clear()清除整个QHash表。

QHash的键和值数据类型必须是可分配的数据类型。例如,您不能将QWidget存储为值;相反,存储一个QWidget *。
qHash()散列函数

5. qHash()的散列函数

QHash内部使用哈希表来索引值的。这个是部分原理

除了是可赋值的数据类型外,QHash的键类型还有其他要求:它必须提供 == 运算符,并且在该类型的命名空间中还必须有一个qHash()函数,该函数返回键类型参数的散列值。

函数qHash()根据键计算数值。它可以使用任何可以想象到的算法,只要给定相同的参数时总是返回相同的值。换句话说,如果e1 == e2,那么qHash(e1) == qHash(e2)也必须成立。然而,为了获得良好的性能,qHash()函数应该尽可能地为不同的键返回不同的散列值。

对于密钥类型K, qHash函数必须具有以下签名之一:

  uint qHash(K key);
  uint qHash(const K &key);

  uint qHash(K key, uint seed);
  uint qHash(const K &key, uint seed);

两个重载参数接受一个无符号整数,该整数将用于散列函数的计算。该种子由QHash提供,用于防止一系列算法复杂性攻击。如果key类型同时定义了单参数和双参数重载,QHash将使用后者(注意,你可以简单地定义一个双参数版本,并为seed参数使用默认值)。
以下是可以作为QHash键的c++和Qt类型的部分列表:任何整数类型(char、unsigned long等)、任何指针类型、QChar、QString和QByteArray。对于所有这些,头文件定义了一个QHash()函数,用于计算适当的散列值。许多其他Qt类也为它们的类型声明了一个qHash重载。请参阅每个类的文档。

如果想使用其他类型作为键,请确保提供==()运算符和qHash()实现。这个就是把 Employee 作为键的例子
例子:

  #ifndef EMPLOYEE_H
  #define EMPLOYEE_H

  class Employee
  {
  public:
      Employee() {}
      Employee(const QString &name, QDate dateOfBirth);
      ...

  private:
      QString myName;
      QDate myDateOfBirth;
  };

  inline bool operator==(const Employee &e1, const Employee &e2)
  {
      return e1.name() == e2.name()
             && e1.dateOfBirth() == e2.dateOfBirth();
  }

  inline uint qHash(const Employee &key, uint seed)
  {
      return qHash(key.name(), seed) ^ key.dateOfBirth().day();
  }

  #endif // EMPLOYEE_H

在上面的例子中,我们依赖Qt的全局qHash(const QString &, uint)来为我们提供员工名字的哈希值,并将其与他们出生的日期异或,以帮助生成具有相同名字的人的唯一哈希值。
请注意,Qt提供的qHash()重载的实现可能随时改变。你不能指望qHash()在不同的Qt版本中(对于相同的输入)会得到相同的结果。

6.算法复杂性

在这里插入图片描述

所有的散列表都容易受到一类特定的拒绝服务攻击,攻击者会仔细地预先计算一组不同的键,这些键将被散列到散列表的同一个桶中(甚至具有相同的散列值)。攻击的目的是在数据被输入到表中时获得最坏情况下的算法行为(O(n),而不是平摊O(1),详情请参阅算法复杂性。这个就是哈希算法函数本身的问题。也就是那个散列函数把多个值计算到同一个位置了。

为了避免这种最坏情况下的行为,可以在qHash()计算哈希值时添加随机种子,从而消除攻击的范围。该种子由QHash在每个进程中自动生成一次,然后作为QHash()函数的两个参数重载的第二个参数传递给QHash。

QHash的这种随机化在默认情况下是启用的。尽管程序永远不应该依赖于特定的QHash排序,但在某些情况下,您可能需要临时确定的行为,例如调试或回归测试。要禁用随机化,可以定义环境变量QT_HASH_SEED的值为0。或者,你可以调用qSetGlobalQHashSeed()函数,参数值为0。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/139076.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

密码学的一些常识01

序 作为一个小白&#xff0c;对称加密&#xff0c;非对称加密&#xff0c;数字签名&#xff0c;数字证书&#xff0c;CA&#xff0c;diff-helman&#xff0c;DES,AED,RSA……这些都不会。所以&#xff0c;百度启动&#xff0c;先初步了解。 实际应用 我是个小白……0基础的&…

sklearn中精确率、召回率及F1值得micro,macro及weighted算法

为什么要用精确率和召回率 有这样一个训练集&#xff0c;1000个人参加了结直肠癌CRC的检测&#xff0c;实际有0.5%的人得了CRC&#xff08;即5个人&#xff09;。用神经网络算法得到检测这样一个训练集能达到99%的准确率。从数值上判断该算法是不错的&#xff0c;因为只有1%的误…

springboot+disruptor再体验

Disruptor是一个高性能队列&#xff0c;常见的还有kafka、rabbitmq等&#xff0c;下面体验一下~ 1、Disruptor简介 Disruptor 是英国外汇交易公司LMAX开发的一个高性能队列&#xff0c;研发的初衷是解决内存队列的延迟问题&#xff08;在性能测试中发现竟然与I/O操作处于同样的…

[C++]STL之string的模拟实现

上一章我们对string的常见接口及使用进行了讲解&#xff0c;接下来我们将对一些常见的接口&#xff0c;包括构造函数&#xff0c;析构函数&#xff0c;运算符重载等等进行模拟实现.方便我们理解string接口实现的原理. 在讲解之前先说一下string的成员变量. 首先是字符串内容_…

微信小程序picker组件遇到的问题以及解决办法

一、picker基本概念二、遇到的问题三、如何解决四、延伸五、效果图一、picker基本概念 先来看一下官方文档中picker的基本概念&#xff1a; 从底部弹起的滚动选择器&#xff0c;现支持三种选择器&#xff0c;通过mode来区分&#xff0c;分别是普通选择器&#xff0c;时间选择器…

Bochs下载安装

文章目录下载Bochs配置BochsBochs Bochs是一个x86硬件平台的开源模拟器。它可以模拟各种硬件的配置。Bochs模拟的是整个PC平台&#xff0c;包括I/O设备、内存和BIOS。更为有趣的是&#xff0c;甚至可以不使用PC硬件来运行Bochs。事实上&#xff0c;它可以在任何编译运行Bochs的…

【Unity3D编辑器扩展】Unity3D中实现Text的字体的替换

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 在开发中会遇到要将场景中的Text的字体全部替换的情况。 所以…

NetInside网络分析帮您解决系统性能问题(一)

前言 某大学信息中心负责人表示&#xff0c;有用户反馈&#xff0c;在通过VPN访问某一IP的80端口时连接时断时续。同时信息中心给到的信息是通过VPN&#xff1a;XXX.XXX.253.5访问IP地址XXX.XXX.130.200的80端口出现访问时断时续问题。 需要通过分析系统看一下实际情况&#…

云原生周刊 | 人类、机器人与 Kubernetes

近日 Grafana 官网发表了一篇博客介绍了 2022 年比较有意思、脑洞大开的一些 Grafana 使用案例&#xff0c;比如监控特斯拉 Model 3 的充电状态、OTA 更新状况等等。 海事技术供应商 Royal IHC 利用 Grafana 展示客户船队的关键性能指标&#xff0c;例如燃料消耗、服务时间、大…

Allegro174版本新功能介绍之打开坐标超链接功能

Allegro174版本新功能介绍之打开坐标超链接功能 Allegro在升级到174的时候默认打开时,报表中的坐标是不带超链接的,如下图 直接点击坐标,是无法自动跳转到坐标所在位置的 但是Allegro174是开放了打开超链接的功能的,具体操作如下 选择Setup选择User Preferences

【 Vue3 + Vite + setup语法糖 + Pinia + VueRouter + Element Plus 第一篇】(持续更新中)

【 Vue3 Vite setup语法糖 Pinia VueRouter Element Plus 第一篇】(持续更新中) 1.使用 Vite脚手架创建 Vue3 项目 终端输入命令 npm create vite 项目名选择 Vue项目并回车根据自己的爱好&#xff0c;选择配置即可 2. 开启 Network 访问地址 npm run dev后 提示 use -…

磨金石教育||商业插画的发展现状如何?学习插画可以月入过万吗?

商业插画是什么&#xff1f;现如今&#xff0c;商业插画已经在生活中随处可见。你买的所有带包装的产品&#xff0c;上面的各种有趣的产品插图&#xff0c;就是插画师做的产品插画。特别是一些零食类的产品&#xff0c;在包装箱上&#xff0c;我们常可以看到各种大眼睛拟人化的…

电脑出现0xc00000e9错误代码的解决方法

每当假期结束回来&#xff0c;经常发现Windows系统的电脑一段时间不开机&#xff0c;开机就出现0xc00000e9的错误代码。为什么明明没有任何操作却出现错误呢&#xff1f;驱动人生带大家一文了解。 出现0xc00000e9错误代码的原因 先来了解一下电脑出现0xc00000e9错误代码的主要…

数字孪生架构

很多同学对数字孪生特别感兴趣&#xff0c;经常有同学问我&#xff1a;数据孪生系统怎么做&#xff1f;有没有教程&#xff1f;除了Unity开发&#xff0c;开发数字孪生还需要掌握什么技能&#xff1f;有人介绍了一个数字孪生的外包&#xff0c;从来没做过&#xff0c;能不能接&…

Spring 中常用的几个工具类

AnnotatedElementUtils 类 获取某个类的某个方法上是否有标注注解&#xff0c;并可以通过其他 API 获取到这个类注解上的属性值&#xff0c;该工具类其他 API 下面截图可以查看。 public static boolean isBeanAnnotated(Method method) {return AnnotatedElementUtils.hasAn…

Redis 应用问题解决

缓存穿透 key 对应的数据在数据源并不存在&#xff0c;每次针对此key的请求从缓存中获取不到&#xff0c;请求会都压到数据源&#xff0c;从而可能压垮数据源。 解决方案 一个一定不存在的缓存及查询不到的数据&#xff0c;由于缓存是不命中时被动写的&#xff0c;并且处于容…

docker 19.03构建跨平台的镜像包并推送到私有仓库

默认的docker构建image镜像是不能跨平台的,如果需要构建跨平台的镜像,需要docker的版本在19.03版本以上,并开启buildx。以下为具体的步骤 版本:docker 19.03。 一.安装/开启 buildx 1.1.手动开启dockerx开关 docker 19.3 暂默认不开启dockerx,需要手动开启 vim /etc/pro…

Scala 数据结构-集合

文章目录Scala 数据结构-集合一、集合简介1、不可变集合继承图2、可变集合继承图二、数组1、不可变数组(1) 创建数组(2) 访问数组(3) 遍历数组(4) 添加元素Scala 数据结构-集合 一、集合简介 1&#xff09;Scala的集合有三大类&#xff1a;序列seq&#xff0c;集合Set&#x…

解决fstab丢失,重启系统变为只读模式

现象描述&#xff1a; 背景&#xff1a;openEuler20.03 在/etc/fstab文件丢失、重启系统后&#xff0c;系统变为只读模式 [rootlocalhost ~]# echo 111 > 1.txt -bash: 1.txt: Read-only file system 解决方法&#xff1a; 查看系统信息&#xff0c;确认挂载信息&#…

【C进阶】数据在内存中的存储

数据在内存中的存储前言一、数据类型介绍&#xff08;一&#xff09;基本概念&#xff08;二&#xff09;类型的基本归类1.整型家族2.浮点型家族3.构造类型4.指针类型5.空类型二、整形在内存中的存储&#xff08;一&#xff09;原码、反码、补码1.概念2.为什么内存中存的是补码…