Qt中丰富的容器类---数组QVector、链表QLinkedList、映射表QMap、哈希表QHash

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介:

在C++里做大型程序时,少不了要与数组、链表等数据结构打交道。就是最简单的字符串也常常让头痛万分,Qt中有QString解决了字符串的头痛,那么其他数组等有没有更简单的解决方案呢?Qt作为一款优秀的类型库,当然不会没考虑这些。Qt提供了大量的“容器类”,专门用于以某种方式存储大量内容,QString其实只是这大量的容器类的一种。

我在这里介绍:

QVector(数组)、QLinkedList(链表)、QMap(映射表)、QHash(哈希表) 

QVector,是Qt对所有数组的封装,比如我们想要一个int类型数组,我们原先会写int array[10],我们在Qt里可以写QVector<int> array(10)

赋值的时候,我们依然可以照旧array[5]=4;想获取某一项的值也还可以array[9],也就是说,原来的特性我们还可以用。

那么QVector有什么好处呢?

·我们可以用count函数获知数组中有多少个元素,方便遍历

·原先我们必须预定义好大小,而用QVector我们虽然最好也先定义好大小,但是预先不定义也可以。

我们可以使用append函数或者<<操作符来在数组最后端添加元素而不用担心溢出问题。

QVector< double > vect(2);
vect[0] = 1.0;
vect[1] = 2.0;
for  ( int  i = 0; i < vect.count(); ++i) {
     cout << vect[i] << endl;
}
 
for  ( int  i = 0; i < vect.count(); ++i) {
     cout << vect.at(i) << endl;
}

要使用索引方式設定元素,必須先配置好夠長的空間,否則會發生超出索引範圍的錯誤,使用[]運算子指定索引存取的方式是比較方便,但在某些場合下,使用at()方法會較有效率一些,這涉及Qt的隱式共享機制,稍後再作介紹。
您也可以使用QVectorappend()方法來加入元素,使用remove()方法來移除元素,使用insert()方法來插入元素,例如append()的使用如下:

vect.append(3.0);vect.append(4.0);

或者是使用<<運算子附加元素:

vect << 5.0 << 6.0;

QVector 也重載了一些其它的運算子,以及提供了一些其它可用的方法,請查詢Qt線上文件有關於QVector的介紹。QVector提供的是鄰接的 記憶體空間以存取物件,所以對於循序存取或使

QList 的子類別QStringListQt中應用很廣的類別,可以讓您儲存QString物件,QList的子類別QQueue則提供了佇列結構的容器管理。
以上先列出QVectorQLinkedListQList的使用比較:

  • 如果想要有連續鄰接的記憶體空間來存放元件,則使用QVector 

  • 如果需要真正的鏈結資料結構,並使用基於迭代器的存取方式,則使用QLinkedList 

  • 在大部份情況下,QList 可以滿足快速存取、插入、移除的需求,並可提供基於索引的存取方式。

QList<QString> list;
list << "caterpillar"  << "momor"  << "bush" ;
     
QListIterator<QString> iterator(list);
while  (iterator.hasNext()) {
     cout << iterator.next().toAscii().data() << endl;
}

  Java 迭代器類似的,hasNext()測試是否有下一個元素,next()傳回下一個元素,其它還有hasPrevious()previous()等方法 可以使用。Java風格的迭代器有兩種:唯讀與可讀寫。QListIterator是唯讀迭代器,對於可讀寫迭代器,命名上會加上Mutable,例 如QMutableListIterator,除了next()previous()等方法之外,還提供了insert()remove()等方法可 以使用,例如:

QLinkedList<QString> list;
  list << "caterpillar"  << "momor"  << "bush" ;
  QMutableLinkedListIterator<QString> rwIterator(list);
while  (rwIterator.hasNext())
  {
  if (rwIterator.next() == "momor" )
  {   rwIterator.insert( "bee" ); 
  break ;
}
}
QLinkedListIterator<QString> rIterator(list);
while  (
rIterator.hasNext()
)
{ cout << rIterator.next().toAscii().data() << endl;
}

 

QMap是个有趣的东西,想在裸露的底层C++实现它颇为麻烦。数组建立的是从0开始的连续数字与数据的对应关系,而QMap的作用就是,让任意一种数据类型对应于另一种数据类型。声明时如此:QMap<索引类型,数据类型> 变量名。他的表现有点类似于PHP编程的array

比如:

#include<QMap>
 
...
 
void  someFunction()
 
{
 
     QMap<QString,QString> map;
 
     map[ "Hello" ]= "World" ;
 
     QMap< int , double > i2d;
 
     i2d[5231]=32.4213;
 
     //遍历比较特殊,得这样:
 
     QMapIterator<QString,QString> i(map);
 
     while (i.hasNext())
 
         doSomething(i.next());
 
}

如果我们想用[]操作符访问某一项,但那一项并不存在,那就会自动创建,如果不想创建空白项可以使用value函数,如i2d.value(123,-0.1);这里如果i2d[123]存在的话就返回那一项,否则返回默认值-0.1,这个默认值可以不写,那样Qt系统就会使用Qt默认的默认值……。可以用take函数(让人纠结的函数名)来删除某一项。 

QHash哈希表,与QMap几乎一样,但是它更高效,不过使用QHash要求作为索引的类型可以用==比较并且有对应的函数qHash,Qt里面自带了一部分,比如QString、各类整数、指针、QByteArray、QChar等都可以直接作为QHash的索引。因为QHash更高效,所以建议尽量使用QHash。 

QMap与QHash都是一对一或多对以的映射,可以使用QMultiMap与QMultiHash建立一对多的映射。

比如QMultiMap <int,QString> map;

map[3]="Hello";

map.insert(3,"World");

调用map[3]时,就会得到一个QList<QString>类型的变量。

遍历时依然可用 QMapIterator

 

本文介绍的是Qt QHash QMap的区别,关于容器类可以查阅更多的资料,首先我们先把QHash QMap区分开来。内容如下。

QMap提供了一个从类项为key的键到类项为T的直的映射,通常所存储的数据类型是一个键对应一个直,并且按照Key的次序存储数据,这个类也支持一键多值的情况,用类QMultiMap

QHash具有和QMap几乎完全一样的APi,此类维护这一张哈希表,表的大小和数据项是自适应的,QHash是以任意的顺序住址他的数据,,当然了他也是可以支持一键多值的,QMultiHash

两种之间的区别是:

QHash查找速度上显著于QMap

QHash以任意的方式进行存储,而QMap则是以key顺序进行存储.

Qhash 的键类型必须提供operator==()和yige 全局的qHash(key)函数。而QMap的键类型key必须提供operator<()函数.

他们同样也是有两种风格的迭代容器。用来进行遍历的。

STL 风格的

QMap<key,T>  QMap<key,T>::const_iterator QMap<key,T>::iterator//同样中间那个也是只读的,最后那个是读写的。下面以一个例子来进行说明:

1.#include <QDebug>
2. int  main( int  argc, char  *argv[])
3.{
4. QMap<QString, QString> map;
5. map.insert( "beijing" , "111" );
6. map.insert( "shanghai" , "021" );
7. map.insert( "tianjin" , "022" );
8. map.insert( "chongqing" , "023" );
9. map.insert( "jinan" , "0531" );
10. map.insert( "wuhan" , "027" );
11.QMap<QString, QString>::const_iterator i;
12. for ( i=map.constBegin(); i!=map.constEnd(); ++i)
13. qDebug() <<i.key() << " "  <<i.value();
14. QMap<QString, QString>::iterator mi;
15.mi = map.find( "beijing" );
16. if (mi != map.end())
17. mi.value() = "010" ;
18. QMap<QString, QString>::const_iterator modi;
19. qDebug() << "" ;
20. for ( modi=map.constBegin(); modi!=map.constEnd(); ++modi)
21. qDebug() <<modi.key() << " "  <<modi.value();
22. return  0;
23.}

  小结:关于Qt 中QHash QMap的区别,相信你看完之后,应该很了然一新了。如果你需要对内存分配做优化,Qt的容器提供了三大内存分配函数,reserve(size),显示预分配size的内存; capacity(),返回已分配内存;squeeze()释放所有的未占用的内存;当你知道QHash的大小时,可以使用reserve函数预先分配内存。 

今天我们来说说Qt容器类中的关联存储容器。所谓关联存储容器,就是容器中存储的一般是二元组,而不是单个的对象。二元组一般表述为,也就是“键-值对”。

  首先,我们看看数组的概念。数组可以看成是一种形式的键-值对,它的Key只能是int,而值的类型是Object,也就是任意类型(注意,这里我们只是说数组可以是任意类型,这个Object并不必须是一个对象)。现在我们扩展数组的概念,把Key也做成任意类型的,而不仅仅是int,这样就是一个关联容器了。如果学过数据结构,典型的关联容器就是散列(Hash Map,哈希表)。Qt提供两种关联容器类型:QMap和QHash。

  QMap是一种键-值对的数据结构,它实际上使用跳表skip-list实现,按照K进行升序的方式进行存储。使用QMap的insert()函数可以向QMap中插入数据,典型的代码如下:

   QMap map; 
map.insert("eins", 1);   map.insert("sieben", 7); 
map.insert("dreiundzwanzig", 23);

 

  同样,QMap也重载了[]运算符,你可以按照数组的复制方式进行使用:

      map["eins"] = 1; 
map["sieben"] = 7; 
map["dreiundzwanzig"] = 23;

 

  []操作符同样也可以像数组一样取值。但是请注意,如果在一个非const的map中,使用[]操作符取一个不存在的Key的值,则这个Key会被自动创建,并将其关联的value赋予一个空值。如果要避免这种情况,请使用QMap的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可以是基本数据类型,如int,double,可以是指针,或者是拥有默认构造函数、拷贝构造函数和赋值运算符的类。并且K必须要重载<运算符,因为QMap需要按K升序进行排序。

  QMap提供了keys()和values()函数,可以获得键的集合和值的集合。这两个集合都是使用QList作为返回值的。

  Map是单值类型的,也就是说,如果一个新的值分配给一个已存在的键,则旧值会被覆盖。如果你需要让一个key可以索引多个值,可以使用QMultiMap。这个类允许一个key索引多个value,如:

   QMultiMap multiMap; 
multiMap.insert(1, "one");   multiMap.insert(1, "eins"); 
multiMap.insert(1, "uno");    
QList vals = multiMap.values(1);

 

  QHash是使用散列存储的键-值对。它的接口同QMap几乎一样,但是它们两个的实现需求不同。QHash的查找速度比QMap快很多,并且它的存储是不排序的。对于QHash而言,K的类型必须重载了==操作符,并且必须被全局函数qHash()所支持,这个函数用于返回key的散列值。Qt已经为int、指针、QChar、QString和QByteArray实现了qHash()函数。

  QHash会自动地为散列分配一个初始大小,并且在插入数据或者删除数据的时候改变散列的大小。我们可以使用reserve()函数扩大散列,使用squeeze()函数将散列缩小到最小大小(这个最小大小实际上是能够存储这些数据的最小空间)。在使用时,我们可以使用reserve()函数将数据项扩大到我们所期望的最大值,然后插入数据,完成之后使用squeeze()函数收缩空间。

  QHash同样也是单值类型的,但是你可以使用insertMulti()函数,或者是使用QMultiHash类来为一个键插入多个值。另外,除了QHash,Qt也提供了QCache来提供缓存,QSet用于仅存储key的情况。这两个类同QHash一样具有K的类型限制。

  遍历关联存储容器的最简单的办法是使用Java风格的遍历器。因为Java风格的遍历器的next()和previous()函数可以返回一个键-值对,而不仅仅是值,例如:

      QMap map;   ... 
int sum = 0;   QMapIterator i(map);   while (i.hasNext()) 
sum += i.next().value();

 

  如果我们并不需要访问键-值对,可以直接忽略next()和previous()函数的返回值,而是调用key()和value()函数即可,如:

 QMapIterator i(map); 
while (i.hasNext()) {    i.next(); 
if (i.value() > largestValue) {    largestKey = i.key(); 
largestValue = i.value();    }   }

 

  Mutable遍历器则可以修改key对应的值:

 QMutableMapIterator i(map); 
while (i.hasNext()) {    i.next();    if (i.value() < 0.0) 
i.setValue(-i.value());   }

 

  如果是STL风格的遍历器,则可以使用它的key()和value()函数。而对于foreach循环,我们就需要分别对key和value进行循环了:

 QMultiMap map;   ... 
foreach (QString key, map.keys()) { 
foreach (int value, map.values(key)) {    doSomething(key, value); 
}   }
本文转自夜&枫博客园博客,原文链接:http://www.cnblogs.com/newstart/archive/2013/05/09/3068625.html,如需转载请自行联系原作者
相关文章
|
4月前
Qt类结构分析
Qt类结构分析
72 3
|
2天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
2月前
|
存储 算法 搜索推荐
探索常见数据结构:数组、链表、栈、队列、树和图
探索常见数据结构:数组、链表、栈、队列、树和图
124 64
|
28天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
54 5
|
2月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
24 0
|
3月前
|
设计模式 前端开发 安全
Qt注册类对象单例与单类型区别
在进行开发时,应当根据具体的应用场景和需求来选择使用单例模式或是单类型。如果是全局服务或状态管理,可能需要单例模式;如果是为了使QML环境下的不同组件能够访问到同一个后端服务对象,则可能需要使用单类型。
52 2
|
4月前
|
存储 开发者 C#
WPF与邮件发送:教你如何在Windows Presentation Foundation应用中无缝集成电子邮件功能——从界面设计到代码实现,全面解析邮件发送的每一个细节密武器!
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中集成电子邮件发送功能,详细介绍了从创建WPF项目到设计用户界面的全过程,并通过具体示例代码展示了如何使用`System.Net.Mail`命名空间中的`SmtpClient`和`MailMessage`类来实现邮件发送逻辑。文章还强调了安全性和错误处理的重要性,提供了实用的异常捕获代码片段,旨在帮助WPF开发者更好地掌握邮件发送技术,提升应用程序的功能性与用户体验。
77 0
|
4月前
|
存储 Java 开发者
揭秘!HashMap底层结构大起底:从数组到链表,再到红黑树,Java性能优化的秘密武器!
【8月更文挑战第24天】HashMap是Java集合框架中的核心组件,以其高效的键值对存储和快速访问能力广受开发者欢迎。在JDK 1.8及以后版本中,HashMap采用了数组+链表+红黑树的混合结构,实现了高性能的同时解决了哈希冲突问题。数组作为基石确保了快速定位;链表则用于处理哈希冲突;而当链表长度达到一定阈值时,通过转换为红黑树进一步提升性能。此外,HashMap还具备动态扩容机制,当负载因子超过预设值时自动扩大容量并重新哈希,确保整体性能。通过对HashMap底层结构的深入了解,我们可以更好地利用其优势解决实际开发中的问题。
125 0
|
18天前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
161 77
|
26天前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序