【Qt之QMap】介绍及示例

描述

QMap类是一个模板类,提供基于红黑树的字典功能。
QMap<Key, T>是Qt中的通用容器类之一。它存储(key, value)键值对,并提供快速查找与特定键相关联的值。
QMapQHash提供非常相似的功能。它们的区别在于:
QHash的查找速度平均比QMap更快。(有关详细信息,请参阅算法复杂度。)

当迭代QHash时,项目的排序是任意的。而使用QMap时,项目总是按键排序的。
QHash的键类型必须提供operator==()和一个全局qHash(Key)函数。QMap的键类型必须提供operator<()来指定一个全序。自Qt 5.8.1起,即使底层的operator<()没有提供全序,也可以安全地将指针类型用作键。

以下是一个使用QString类型键和int类型值的示例QMap

cpp 复制代码
  QMap<QString, int> map;

要将(key, value)对插入到map中,可以使用operator<> :

cpp 复制代码
  map["one"] = 1;
  map["three"] = 3;
  map["seven"] = 7;

这将把以下三个(key, value)对插入到QMap中:("one", 1), ("three", 3)和("seven", 7)。另一种插入项到map的方法是使用insert():

cpp 复制代码
  map.insert("twelve", 12);

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

cpp 复制代码
  int num1 = map["thirteen"];
  num2 = map.value("thirteen");

如果在map中不存在指定的键,这些函数将返回一个默认构造的值。

如果想要检查map是否包含某个特定的键,可以使用contains():

cpp 复制代码
  int timeout = 30;
  if (map.contains("TIMEOUT"))
      timeout = map.value("TIMEOUT");

还有一个value()重载,如果map中不存在指定的键,可以使用其第二个参数作为默认值:

cpp 复制代码
  int timeout = map.value("TIMEOUT", 30);

一般来说,我们建议在map中查找键时使用contains()和value(),而不是operator<>。原因是operator<>如果没有具有相同键的项(除非map是const),会默默地向map中插入一个项。例如,下面的代码片段将在内存中创建1000个项:

cpp 复制代码
  // 错误
  QMap<int, QWidget *> map;
  ...
  for (int i = 0; i < 1000; ++i) {
      if (map[i] == okButton)
          cout << "Found button at index " << i << endl;
  }

为避免这个问题,在上面的代码中将map[i]替换为map.value(i)。

如果想要遍历存储在QMap中的所有(key, value)对,可以使用迭代器。QMap提供Java风格的迭代器(QMapIterator和QMutableMapIterator)和STL风格的迭代器(QMap::const_iterator和QMap::iterator)。以下是如何使用Java风格迭代器遍历QMap<QString, int>的示例:

cpp 复制代码
  QMapIterator<QString, int> i(map);
  while (i.hasNext()) {
      i.next();
      cout << i.key() << ": " << i.value() << endl;
  }

这是相同的代码,但是这次使用STL风格的迭代器:

cpp 复制代码
  QMap<QString, int>::const_iterator i = map.constBegin();
 while (i != map.constEnd()) {
      cout << i.key() << ": " << i.value() << endl;
      ++i;
  }

项目按键的升序顺序遍历。

通常,QMap允许每个键只有一个值。如果您使用已经存在于QMap中键调用insert(),则先前的值将删除。例如:

cpp 复制代码
  map.insert("plenty", 100);
  map.insert("enty", 2000);
  // map.value("plenty") == 2000

然而,您可以通过使用insertMulti()而不是insert()(或使用便利子类QMultiMap)来为每个键存储多个值如果要检索单个键的所有值,可以使用values(const Key &key),它返回一个QList:

cpp 复制代码
  QList<int> values = map.values("plenty");
  for (int i = 0; i < values.size(); ++i)
      cout << values(i) << endl;

具有相同键的项从最近插入到最早插入可用。另一种方法是调用find()以获取具有键的第一个项的STL风格迭代器,并从那里进行迭代:

cpp 复制代码
  QMap<QString, int>::iterator i = map.find("plenty");
  while (i != map.end() && i.key() == "plenty") {
      cout << i.value() << endl;
      ++i;
  }

如果只需要从中提取值(而是键),还可以foreach:

cpp 复制代码
  QMap<QString, int> map;
  ...
  foreach (int value map)
      cout << value << endl;

可以通过多种方式从map中删除项。一种方法是调用remove();这将删除具有给定键的任何项。另一种方法是使用QMutableMapIterator::remove()此外,您可以使用clear()来清除整个map。

QMap的键和值数据类型必须是可赋值的数据类型。这涵盖了您可能遇到的大多数数据类型,但编译器不会让您以值的形式存储QWidget;反而,存储QWidget *。此外,QMap的键类型必须提供operator<()。QMap使用它来保持其项目排序,并假定如果x < y也没有y < x,则两个x和y相等。

示例:

cpp 复制代码
  #ifndef EMPLOYEE_H
  #define EMPLOYEE_H

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

  private:
      QString myName;
      QDate myDateOfBirth;
  };

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

  #endif // EMPLOYEE_H

在示例中,首先比较员工的姓名如果它们相等,比较它的出生日期以打平局。

常用方法

下面是常用的QMap方法的介绍:

  • insert(key, value):将一个(key, value)对插入到map中。如果key已经存在于map中,则会覆盖原有的value。
  • insertMulti(key, value):将一个(key, value)对插入到map中,允许一个key对应多个value。可以通过values(key)方法获取所有与key对应的value列表。
  • remove(key):删除map中指定的key及其对应的value。
  • clear():清空map,移除其中的所有项。
  • contains(key):判断map中是否存在指定的key。
  • value(key, defaultValue):返回与指定的key关联的value。如果key不存在于map中,则返回默认值defaultValue。
  • count():返回map中键值对的数量。
  • keys():返回map中所有的键构成的列表。
  • values():返回map中所有的值构成的列表。
  • isEmpty():判断map是否为空,即是否不包含任何键值对。
  • size():返回map中键值对的数量。
  • begin() / constBegin():返回指向map中第一个键值对的迭代器。
  • end() / constEnd():返回指向map中最后一个键值对之后位置的迭代器。
  • find(key):查找map中第一个具有指定key的键值对,并返回指向该键值对的迭代器。
  • iterator / const_iterator:提供STL风格的迭代器,可以用于遍历map中的键值对。

erase()方法

QMaperase()方法用于从map中删除指定的键值对。用法如下:

  • erase(iterator):删除迭代器指向的键值对,并返回指向下一个键值对的迭代器。
    例如:
cpp 复制代码
QMap<QString, int> map;
map.insert("key1", 10);
map.insert("key2", 20);

QMap<QString, int>::iterator it = map.begin();
it = map.erase(it); // 删除迭代器指向的键值对,返回指向下一个键值对的迭代器
// 现在it指向键为"key2"的键值对

需要注意的是,当使用erase()方法删除元素后,迭代器会失效,如果还需要继续遍历map,应该使用返回的迭代器继续操作。

示例

cpp 复制代码
#include <QMap>
#include <QString>
#include <QDebug>

int main() {
    QMap<QString, int> map;
    
    // 添加键值对
    map.insert("One", 1);
    map.insert("Two", 2);
    map.insert("Three", 3);
    
    // 遍历并打印map中的键值对
    qDebug() << "Map content:";
    QMap<QString, int>::const_iterator iter;
    for (iter = map.constBegin(); iter != map.constEnd(); ++iter) {
        qDebug() << iter.key() << ":" << iter.value();
    }
    
    // 删除指定的键值对
    map.remove("Two");
    
    // 查找键为"Three"的键值对
    qDebug() << "Value of key 'Three':";
    if (map.contains("Three")) {
        qDebug() << map.value("Three");
    } else {
        qDebug() << "Key not found.";
 }
    
    // 获取所有键
    qDebug() << "All keys:";
    QList<QString> keys = map.keys();    
    foreach (const QString& key, keys) {
        qDebug() << key;
    }
    
    // 获取所有值
    qDebug() << "All values:";
    QList<int> values = map.values();    
    foreach (int value, values) {
        qDebug() << value;
    }
    
    return 0;
}

运行以上示例代码,输出结果如下:

cpp 复制代码
Map content:
"One" : 1
"Two" : 2
"Three" : 3

Value of key 'Three':
3

All keys:
"One"
"Three"

All values:
1
3

注意

cpp 复制代码
        QMap<int, int*> map;
        
        QMap<int, int*>::iterator it = map.begin();
        while (it != map.end()) {
            map.erase(it);
            
            ++it;
        }

以上使用QMap的erase()是错误的。

应改为:

cpp 复制代码
        QMap<int, int*> map;
        
        map[1] = new int(1);
        map[2] = new int(2);
        map[3] = new int(3);
        
        QMap<int, int*>::iterator it = map.begin();
        while (it != map.end()) {
        	// 接受返回值,进行循环
            it = map.erase(it);
        }
相关推荐
Karoku0663 分钟前
【企业级分布式系统】ELK优化
运维·服务器·数据库·elk·elasticsearch
徐霞客3201 小时前
Qt入门1——认识Qt的几个常用头文件和常用函数
开发语言·c++·笔记·qt
小技与小术1 小时前
数据库表设计范式
数据库·mysql
安迁岚1 小时前
【SQL Server】华中农业大学空间数据库实验报告 实验三 数据操作
运维·服务器·数据库·sql·mysql
安迁岚1 小时前
【SQL Server】华中农业大学空间数据库实验报告 实验九 触发器
数据库·sql·mysql·oracle·实验报告
Loganer1 小时前
MongoDB分片集群搭建
数据库·mongodb
LKID体1 小时前
Python操作neo4j库py2neo使用之创建和查询(二)
数据库·python·neo4j
vmlogin虚拟多登浏览器2 小时前
虚拟浏览器可以应对哪些浏览器安全威胁?
服务器·网络·安全·跨境电商·防关联
姆路2 小时前
QT Designer内存飙升
qt
刘大浪2 小时前
后端数据增删改查基于Springboot+mybatis mysql 时间根据当时时间自动填充,数据库连接查询不一致,mysql数据库连接不好用
数据库·spring boot·mybatis