Qt学习之路(37): Qt容器类之关联存储容器QMap和QHash

Qt学习之路(37): Qt容器类之关联存储容器
2009-12-24 13:58:58
标签: C++  教程  学习  QT教程  Qt
原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 http://devbean.blog.51cto.com/448512/248373
今天我们来说说Qt容器类中的关联存储容器。所谓关联存储容器,就是容器中存储的一般是二元组,而不是单个的对象。二元组一般表述为<Key-Value>,也就是“键-值对”。
 
首先,我们看看数组的概念。数组可以看成是一种<int-Object>形式的键-值对,它的Key只能是int,而值的类型是Object,也就是任意类型(注意,这里我们只是说数组可以是任意类型,这个Object并不必须是一个对象)。现在我们扩展数组的概念,把Key也做成任意类型的,而不仅仅是int,这样就是一个关联容器了。如果学过数据结构,典型的关联容器就是散列(Hash Map,哈希表)。Qt提供两种关联容器类型:QMap<K, T>和QHash<K, T>。
 
QMap<K, T>是一种键-值对的数据结构,它实际上使用跳表skip-list实现,按照K进行升序的方式进行存储。使用QMap<K, T>的insert()函数可以向QMap<K, T>中插入数据,典型的代码如下:
 
QMap<QString,  int> map; 
map.insert( "eins", 1); 
map.insert( "sieben", 7); 
map.insert( "dreiundzwanzig", 23);
 
同样,QMap<K, T>也重载了[]运算符,你可以按照数组的复制方式进行使用:
 
map[ "eins"] = 1; 
map[ "sieben"] = 7; 
map[ "dreiundzwanzig"] = 23;
 
[]操作符同样也可以像数组一样取值。但是请注意,如果在一个非const的map中,使用[]操作符取一个不存在的Key的值,则这个Key会被自动创建,并将其关联的value赋予一个空值。如果要避免这种情况,请使用QMap<K, T>的value()函数:
 
int val = map.value("dreiundzwanzig");
 
如果key不存在,基本类型和指针会返回0,对象类型则会调用默认构造函数,返回一个对象,与[]操作符不同的是,value()函数不会创建一个新的键-值对。如果你希望让不存在的键返回一个默认值,可以传给value()函数第二个参数:
 
int seconds = map.value("delay", 30);
 
这行代码等价于:
 
int seconds = 30; 
if (map.contains("delay")) 
        seconds = map.value("delay");
 
QMap<K, T>中的K和T可以是基本数据类型,如int,double,可以是指针,或者是拥有默认构造函数、拷贝构造函数和赋值运算符的类。并且K必须要重载<运算符,因为QMap<K, T>需要按K升序进行排序。
 
QMap<K, T>提供了keys()和values()函数,可以获得键的集合和值的集合。这两个集合都是使用QList作为返回值的。
 
Map是单值类型的,也就是说,如果一个新的值分配给一个已存在的键,则旧值会被覆盖。如果你需要让一个key可以索引多个值,可以使用QMultiMap<K, T>。这个类允许一个key索引多个value,如:
 
QMultiMap< int, QString> multiMap; 
multiMap.insert(1,  "one"); 
multiMap.insert(1,  "eins"); 
multiMap.insert(1,  "uno"); 
 
QList<QString> vals = multiMap.values(1);
 
QHash<K, T>是使用散列存储的键-值对。它的接口同QMap<K, T>几乎一样,但是它们两个的实现需求不同。QHash<K, T>的查找速度比QMap<K, T>快很多,并且它的存储是不排序的。对于QHash<K, T>而言,K的类型必须重载了==操作符,并且必须被全局函数qHash()所支持,这个函数用于返回key的散列值。Qt已经为int、指针、QChar、QString和QByteArray实现了qHash()函数。
 
QHash<K, T>会自动地为散列分配一个初始大小,并且在插入数据或者删除数据的时候改变散列的大小。我们可以使用reserve()函数扩大散列,使用squeeze()函数将散列缩小到最小大小(这个最小大小实际上是能够存储这些数据的最小空间)。在使用时,我们可以使用reserve()函数将数据项扩大到我们所期望的最大值,然后插入数据,完成之后使用squeeze()函数收缩空间。
 
QHash<K, T>同样也是单值类型的,但是你可以使用insertMulti()函数,或者是使用QMultiHash<K, T>类来为一个键插入多个值。另外,除了QHash<K, T>,Qt也提供了QCache<K, T>来提供缓存,QSet<K>用于仅存储key的情况。这两个类同QHash<K, T>一样具有K的类型限制。
 
遍历关联存储容器的最简单的办法是使用Java风格的遍历器。因为Java风格的遍历器的next()和previous()函数可以返回一个键-值对,而不仅仅是值,例如:
 
QMap<QString,  int> map; 
... 
int sum = 0; 
QMapIterator<QString,  int> i(map); 
while (i.hasNext()) 
        sum += i.next().value();
 
如果我们并不需要访问键-值对,可以直接忽略next()和previous()函数的返回值,而是调用key()和value()函数即可,如:
 
QMapIterator<QString,  int> i(map); 
while (i.hasNext()) { 
        i.next(); 
         if (i.value() > largestValue) { 
                largestKey = i.key(); 
                largestValue = i.value(); 
        } 
}
 
Mutable遍历器则可以修改key对应的值:
 
QMutableMapIterator<QString,  int> i(map); 
while (i.hasNext()) { 
        i.next(); 
         if (i.value() < 0.0) 
                i.setValue(-i.value()); 
}
 
如果是STL风格的遍历器,则可以使用它的key()和value()函数。而对于foreach循环,我们就需要分别对key和value进行循环了:
 
QMultiMap<QString,  int> map; 
... 
foreach (QString key, map.keys()) { 
         foreach ( int value, map.values(key)) { 
                doSomething(key, value); 
        } 

本文出自 “豆子空间” 博客,请务必保留此出处http://devbean.blog.51cto.com/448512/248373

分享至
一键收藏,随时查看,分享好友!
 qustdjx、jnblue、我的心8023
3人
了这篇文章
类别: Qt┆阅读( 13546)┆评论( 4) ┆  返回博主首页┆ 返回博客首页
上一篇  Qt学习之路(36): Qt容器类之遍历器和隐式数据共享 下一篇  Qt学习之路(38): model-view架构

QT QMap介绍与使用

2011-06-22 13:27 佚名 互联网  字号: T |  T
一键收藏,随时查看,分享好友!

本文介绍的是QMap介绍与使用,值得一提的问题是QMap的值和键是一对一对的,必须要一对一对的插入。

AD:【活动】Web和APP兼容性实战 Win10训练营免费报名

Qt中的QMap介绍与使用,在坛子里逛了一圈,发现在使用QMap中,出现过很多的问题,Map是一个很有用的数据结构。它以“键-值”的形式保存数据。在使用的时候,通过提供字符标示(键)即可得到想要的数据。这个“数据”即可以是一个字符串,也可以是任意对象,当然也包括自己定义的类对象。说明:map是以值传递的形式保存数据的。

1. 基本应用

下面以“键-值”都是QString的例子说明QMap的基本使用方法。更详细的说明,请查看《Qt帮助手册》或其他资源。

         
         
         
         
  1. #include <qmap.h> 
  2. #include <iostream> 
  3. using namespace std;  
  4. class MapTest  
  5. {  
  6. public:  
  7.     void showMap()  
  8.     {  
  9. if(!m_map.isEmpty()) return; //判断map是否为空  
  10. m_map.insert("111", "aaa"); //向map里添加一对“键-值”  
  11. if(!m_map.contains("222")) //判断map里是否已经包含某“键-值”  
  12.      m_map.insert("222", "bbb");  
  13. m_map["333"] = "ccc"; //另一种添加的方式  
  14. qDebug("map[333] , value is : " + m_map["333"]); //这种方式既可以用于添加,也可以用于获取,但是你必须知道它确实存在  
  15.  
  16. if(m_map.contains("111")){  
  17.      QMap<QString,QString>::iterator it = m_map.find("111"); //找到特定的“键-值”对  
  18.      qDebug("find 111 , value is : " + it.data()); //获取map里对应的值  
  19. }  
  20. cout<< endl;  
  21. qDebug("size of this map is : %d", m_map.count()); //获取map包含的总数  
  22. cout<< endl;  
  23. QMap<QString,QString>::iterator it; //遍历map  
  24. for ( it = m_map.begin(); it != m_map.end(); ++it ) {  
  25.             qDebug( "%s: %s", it.key().ascii(), it.data().ascii()); //用key()和data()分别获取“键”和“值”  
  26.         }  
  27.  
  28. m_map.clear(); //清空map  
  29.     }  
  30. private:  
  31.     QMap<QString,QString> m_map; //定义一个QMap对象  
  32. }; 

调用类函数showMap(),显示结果:

         
         
         
         
  1. map[333] , value is : ccc  
  2. find 111 , value is : aaa  
  3. size of this map is : 3  
  4. 111: aaa  
  5. 222: bbb  
  6. 333: ccc 

2. 对象的使用

map当中还可以保存类对象、自己定义类对象,例子如下(摘自QT帮助文档《Qt Assistant》,更详细的说明参考之):
以注释形式说明

         
         
         
         
  1. #include <qstring.h> 
  2. #include <qmap.h> 
  3. #include <qstring.h> 
  4.  
  5. //自定义一个Employee类,包含fn、sn、sal属性  
  6. class Employee  
  7. {  
  8. public:  
  9.     Employee(): sn(0) {}  
  10.     Employee( const QString& forename, const QString& surname, int salary )  
  11.         : fn(forename), sn(surname), sal(salary)  
  12.     { }  
  13.  
  14.     QString forename() const { return fn; }  
  15.     QString surname() const { return sn; }  
  16.     int salary() const { return sal; }  
  17.     void setSalary( int salary ) { sal = salary; }  
  18.  
  19. private:  
  20.     QString fn;  
  21.     QString sn;  
  22.     int sal;  
  23. };  
  24.  
  25. int main(int argc, char **argv)  
  26. {  
  27.     QApplication app( argc, argv );  
  28.  
  29.     typedef QMap<QString, Employee> EmployeeMap; //自定义一个map类型,值为EmployeeMap对象  
  30.     EmployeeMap map;  
  31.  
  32.     map["JD001"] = Employee("John", "Doe", 50000); //向map里插入键-值  
  33.     map["JW002"] = Employee("Jane", "Williams", 80000);  
  34.     map["TJ001"] = Employee("Tom", "Jones", 60000);  
  35.  
  36.     Employee sasha( "Sasha", "Hind", 50000 );  
  37.     map["SH001"] = sasha;  
  38.     sasha.setSalary( 40000 ); //修改map值的内容,因为map采用值传递,所以无效  
  39.  
  40.     //批量打印  
  41.     EmployeeMap::Iterator it;  
  42.     for ( it = map.begin(); it != map.end(); ++it ) {  
  43.         printf( "%s: %s, %s earns %d\n",  
  44.                 it.key().latin1(),  
  45.                 it.data().surname().latin1(),  
  46.                 it.data().forename().latin1(),  
  47.                 it.data().salary() );  
  48.     }  
  49.     return 0;  
         
         
         
         
  1. Program output:   
  2. JD001: Doe, John earns 50000  
  3. JW002: Williams, Jane earns 80000  
  4. SH001: Hind, Sasha earns 50000  
  5. TJ001: Jones, Tom earns 60000 

小结:QMap介绍与使用的内容介绍完了,基本是在讲QMap的使用,那么通过本文希望你能了解更多关于QMap的知识。

【编辑推荐】

  1. 区分 Qt QHash 和QMap

你可能感兴趣的:(Qt学习之路(37): Qt容器类之关联存储容器QMap和QHash)