前言
??Qt提供了一组通用的基于模板的容器类。 ??优点:相比C++的标准模板库中的容器类,Qt的容器更轻量、更安全且更容易使用。Qt的容器类在速度、内存消耗和内联(inline)代码等方面进行了优化。 ??Qt容器类中存储的数据必须是可赋值的数据类型,也就是说:这种数据类型必须提供一个默认的构造函数(不需要参数的构造函数)、一个复制构造函数和一个赋值操作运算符。 ??这样的数据类型包括基本数据类型(int和double等)和Qt的一些数据类型(QString、QDate和QTime等);也可存储QObject及其子类的指针,例如:
QList<QToolBar*> list;
Qt的容器类是可以嵌套的,例如:
QHash< QString, QList<double> >
??Qt的容器类为遍历其中内容,提供两种方法: (1)Java风格的迭代器 (2)STL风格的迭代器;能同Qt和STL的通用算法一起用,效率更高。
一、QList类、QLinkedList类和QVector类
时间复杂度比较
容器类 | 查找 | 插入 | 头部添加 | 尾部添加 |
---|
QList | O(1) | O(n) | Amort.O(1) | Amort.O(1) | QLinkedList | O(n) | O(1) | O(1) | O(1) | QVector | O(1) | O(n) | O(n) | Amort.O(1) |
其中,"Amort.O(1)"表示,若仅完成一次操作,可能会有O(n)行为;若完成多次操作,平均结果将是O(1)。
1.QList类
??QList<T>是最常用的容器类,它存储给定数据类型T的一列数值。继承自QList类的子类有QItemSelection、QQueue、QSignalSpy、QStringList和QTestEventList。 ??QList类提供了在列表中追加的函数(QList::append()和QList::prepend());在列表中间完成插入操作的函数(QList::insert())。 ??QList<T>维护了一个指针数组,该数组存储的指针指向QList<T>存储的列表项的内容;QList<T>提供基于下标的快速访问。 ??存储策略有以下几种: ?(1)若T是指针类型或指针大小的基本类型(即该基本类型占有的字节数和指针类型占有的字节数相同),QList<T>会将数值直接存储在数组中。 ?(2)若QList<T>存储对象的指针,则该指针指向实际存储的对象。
2.QLinkedList类
??QLinkedList<T>是一个链式列表,它以非连续的内存块保存数据。 ??QLinkedList<T>不能使用下标,只能使用迭代器访问它的数据项。 ??对一个很大的列表进行插入操作时,QLinkedList比QList效率更高。
3.QVector类
??QVector<T>在相邻的内存中存储给定数据类型T的一组数值。 ??QVector<T>既可以使用下标访问数据项,也可以使用迭代器访问数据项;继承的子类有QPolygon、QPolygonF和QStack。 ??缺点:在QVector的前部或中间位置进行插入操作的速度是很慢的,因为此操作会导致内存中大量数据被移动。
4.Java风格迭代器遍历容器
??Qt提供了两种类型的Java风格迭代器数据类型,即只读迭代器类和读写迭代器类,如下表:
容器类 | 只读迭代器类 | 读写迭代器类 |
---|
QList<T> , QQueue<T> | QListIterator<T> | QMutableListIterator<T> | QLinkedList<T> | QLinkedListIterator<T> | QMutableLinkedListIterator<T> | QVector<T> , QStack<T> | QVectorIterator<T> | QMutableVectorIterator<T> |
??Java风格迭代器的迭代点位于列表项的中间,而不是直接指向某个列表项。因此,它的迭代点或者在第一个列表项的前面,或者在两个列表项之间,或者在最后一个列表项之后。 ??以QList为例,介绍两种Java风格迭代器的用法: (1)QList只读遍历方法。 代码如下(示例):
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list;
list<<1<<2<<3<<4<<5;
QListIterator<int> i(list);
for(; i.hasNext(); )
qDebug()<<i.next();
return a.exec();
}
?(a)头文件<QDebug>中已经包含了QList的头文件 ?(b)Qt的一些类,如QString、QList等,不需要QCoreApplication的支持也能工作,但在使用Qt编写应用程序时,如果是控制台应用程序,则建议初始化一个QCoreApplication对象;如果是GUI图形用户界面程序,则会初始化一个QApplication对象 ?(c)QListIterator<int> i(list):以该list为参数初始化一个QListIterator对象i;此时迭代点处在第一个列表项"1"的前面(注意:并不是指向该列表项) ?(d)for(; i.hasNext(); ):调用QListIterator::hasNext()函数检查当前迭代点之后是否有列表项;如果有则调用QListIterator::next()函数进行遍历。next()函数会跳过下一个列表项(即迭代点将会位于第一个列表项和第二个列表项之间),并返回它跳过的列表项的内容。 ?对列表项向前遍历的函数: ??QListIterator<T>::toBack():将迭代点移动到最后一个列表项的后面。 ??QListIterator<T>::hasPrevious():检查当前迭代点之前是否具有列表项。 ??QListIterator<T>::previous():返回前一个列表项的内容并将迭代点移动到前一个列表项之前。 ?其他函数: ??toFront():移动迭代点到列表的前端(第一个列表项的前面)。 ??peekNext():返回下一个列表项,但不移动迭代点。 ??peekPrevious():返回前一个列表项,但不移动迭代点。 ??findNext():从当前迭代点开始向后查找指定的列表项,找到返回true,此时迭代点位于匹配列表项的后面;未找到返回false,此时迭代点位于列表的后端(最后一个列表项的后面)。 ??findPrevious():与findNext()类似,但它的方向是向前的,查找完成后迭代点在匹配想的前面或整个列表的前端。 (2)读写迭代器QMutableListIterator<T>除提供基本的遍历操作(与QListIterator的操作相同)外,还提供了insert()插入操作函数、remove()删除操作函数和修改数据函数等。 代码如下(示例):
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list;
QMutableListIterator<int> i(list);
for(int j = 0; j < 10; ++j)
i.insert(j);
for(i.toFront(); i.hasNext(); )
qDebug() << i.next();
for(i.toBack(); i.hasPrevious(); )
{
if(i.previous()%2 == 0)
i.remove();
else
i.setValue(i.peekNext()*10);
}
for(i.toFront(); i.hasNext(); )
qDebug() << i.next();
return a.exec();
}
?(a)i.insert(j):通过QMutableListIterator<T>::insert()插入操作,为该列表插入10个整数值。 ?(b)将迭代器的迭代点移动到列表的前端,完成对列表的遍历和输出。 ?(c)移动迭代器的迭代点到列表的后端,对列表进行遍历。如果前一个列表项的值为偶数,则将该列表项删除;否则,将该列表项的值修改为原来的10倍。 ?(d)函数QMutableListIterator<T>::setValue()修改遍历函数next()、previous()、findNext()、findPrevious()跳过的列表项的值,但不会移动迭代点的位置。对于findNext()和findPrevious():当findNext()(或findPrevious())查找到列表项的时候,setValue()将会修改匹配的列表项;若未找到,则对setValue()的调用将不会进行任何修改。
5.STL风格迭代器遍历容器
??对于每个容器类,Qt都提供了两种类型的STL风格迭代器数据类型:一种提供只读访问;一种提供读写访问。 ??STL风格迭代器的API是建立在指针操作基础上的。如,"++“操作运算符移动迭代器到下一项(item),而”*"操作运算符返回迭代器指向的项。 ??STL风格迭代器的迭代点直接指向列表项。 STL风格迭代器的两种分类:
容器类 | 只读迭代器类 | 读写迭代器类 |
---|
QList<T> , QQueue<T> | QList<T>::const_iterator | QList<T>::iterator | QLinkedList<T> | QLinkedList<T>::const_iterator | QLinkedList<T>::iterator | QVector<T> , QStack<T> | QVector<T>::const_iterator | QVector<T>::iterator |
代码如下(示例):
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list;
for(int j = 0; j < 10; j++)
list.insert(list.end(), j);
QList<int>::iterator i;
for(i = list.begin(); i != list.end(); ++i)
{
qDebug() << (*i);
*i = (*i) * 10;
}
QList<int>::const_iterator ci;
for(ci = list.constBegin(); ci != list.constEnd(); ++ci)
qDebug() << *ci;
return a.exec();
}
?(a)使用QList<T>::insert()函数插入10个整数值。函数两个参数:第一个参数是QList<T>::iterator类型,表示在该列表项之前插入一个新的列表项(使用QList<T>::end()函数返回的迭代器,表示在列表的最后插入一个列表项);第二个参数指定了需要插入的值。 ?(b)在控制台输出列表的同时将列表的所有值增大10倍。QList<T>::begin()函数返回指向第一个列表项的迭代器;QList<T>::end()函数返回一个容器最后列表项之后的虚拟列表项,为标记无效位置的迭代器,用于判断是否到达容器的底部。 ??QLinkedList和QVector具有和QList相同的遍历接口。
二、QMap类和QHash类
??QMap类和QHash类具有类似的功能,差别仅在于:
- QHash具有比QMap更快的查找速度;
- QHash以任意顺序存储数据项,而QMap按照键Key的顺序存储数据;
- QHash的键类型Key必须提供operator==()和一个全局的qHash(Key)函数,而QMap的键类型Key必须提供operator<()函数。
QMap和QHash的时间复杂度比较:
容器类 | 键查找 | 键查找 | 插入 | 插入 |
---|
容器类 | 平均 | 最坏 | 平均 | 最坏 | QMap | O(log n) | O(log n) | O(log n) | O(log n) | QHash | Amort.O(1) | O(n) | Amort.O(1) | O(n) |
??其中,"Amort.O(1)"表示,若仅完成一次操作,则可能会有O(n)行为;若完成多次操作(如n次),则平均结果将是O(1)。
1.QMap类
??QMap<Key, T>提供了一个从类型为Key的键到类型为T的值的映射。 ??通常,QMap存储的数据形式是一个键对应一个值,且按照键Key的顺序存储数据。为了支持一键多值,QMap提供了QMap<Key, T>::insertMulti()和QMap<Key, T>::values()函数。存储一键多值的数据时,也可以使用QMultiMap<Key, T>容器,继承自QMap。
2.QHash类
??QHash<Key, T>具有与QMap几乎相同的API。QHash维护着一张哈希表(Hash Table),哈希表的大小与QHash的数据项的数目相适应。 ??QHash以任意顺序组织数据。当存储数据的顺序无关紧要时,建议使用QHash作为存放数据的容器。 ??QHash也可以存储一键多值形式的数据,子类QMultiHash<Key, T>可实现。
3.Java风格迭代器遍历容器
Java风格迭代器的两种分类:
容器类 | 只读迭代器类 | 读写迭代器类 |
---|
QMap<Key, T> , QMultiMap<Key, T> | QMapIterator<Key, T> | QMutableMapIterator<Key, T> | QHash<Key, T> , QMultiHash<Key, T> | QHashIterator<Key, T> | QMutableHashIterator<Key, T> |
代码如下(示例):
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QMap<QString, QString> map;
map.insert("beijing", "111");
map.insert("shanghai", "021");
map.insert("nanjing", "025");
QMapIterator<QString, QString> i(map);
for( ; i.hasNext(); )
{
i.next();
qDebug() << " " << i.key() << " " << i.value();
}
QMutableMapIterator<QString, QString> mi(map);
if(mi.findNext("111"))
mi.setValue("010");
QMapIterator<QString, QString> modi(map);
qDebug() << " ";
for( ; modi.hasNext(); )
{
modi.next();
qDebug() << " " << modi.key() << " " << modi.value();
}
return a.exec();
}
(a)完成对QMap的遍历输出。在输出QMap的键和值时,调用的函数是不同的,在调用函数前必须先将迭代点移动到下一位置。 (b)首先查找<键,值>对,然后修改值。Java风格的迭代器未提供查找键的函数。
4.STL风格迭代器遍历容器
STL风格迭代器的两种分类:
容器类 | 只读迭代器类 | 读写迭代器类 |
---|
QMap<Key, T> , QMultiMap<Key, T> | QMap<Key, T>::const_iterator | QMap<Key, T>::iterator | QHash<Key, T> , QMultiHash<Key, T> | QHash<Key, T>::const_iterator | QHash<Key, T>::iterator |
代码如下(示例):
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QMap<QString, QString> map;
map.insert("beijing", "111");
map.insert("shanghai", "021");
map.insert("nanjing", "025");
QMap<QString, QString>::const_iterator i;
for(i = map.constBegin(); i != map.constEnd(); ++i)
qDebug() << " " << i.key() << " " << i.value();
QMap<QString, QString>::iterator mi;
mi = map.find("beijing");
if(mi != map.end())
mi.value() = "010";
QMap<QString, QString>::const_iterator modi;
qDebug() << " ";
for(modi = map.constBegin(); modi != map.constend(); ++modi)
qDebug() << " " << modi.key() << " " << modi.value();
return a.exec();
}
(a)将新的值直接赋给QMap<QString, QString>::iterator::value()返回的结果,因为该函数返回的是<键,值>对其中值的引用。
三、QVariant类
??QVariant类类似于C++的联合(union)数据类型,不仅能保存Qt类型的值,包括QColor、QBrush、QFont、QPen、QRect、QString、QSize等;也能存放Qt的容器类型的值。Qt的很多功能都建立在QVariant基础上,如Qt的对象属性及数据库功能等。 代码如下(示例):
#include "widget.h"
#include <QDebug>
#include <QVariant>
#include <QColor>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QVariant v(709);
qDebug() << v.toInt();
QVariant w("How are you! ");
qDebug() << w.toString();
QMap<QString, QVariant>map;
map["int"] = 709;
map["double"] = 709.709;
map["string"] = "How are you! ";
map["color"] = QColor(255,0,0);
qDebug() << map["int"] << map["int"].toInt();
qDebug() << map["double"] << map["double"].toDouble();
qDebug() << map["string"] << map["string"].toString();
qDebug() << map["color"] << map["color"].value<QColor>();
QStringList sl;
sl << "A" << "B" << "C" << "D";
QVariant slv(sl);
if(slv.type() == QVariant::StringList)
{
QStringList list = slv.toStringList();
for(int i = 0; i < list.size(); ++i)
qDebug() << list.at(i);
}
}
Widget::~Widget()
{
}
(a)声明一个QVariant变量v,并初始化一个整数。 (b)调用QVariant::toInt()函数将QVariant变量包含的内容转换为整数并输出。 (c)声明一个QVariant变量w,并初始化为一个字符串。 (d)调用QVariant::toString()函数将QVariant变量包含的内容转换为字符串并输出。 (e)声明一个QMap变量map,使用字符串作为键,QVariant变量作为值。 (f)在QVariant变量中保存了一个QColor对象,并使用模板QVariant::value()还原为QColor,然后输出。由于QVariant是QCore模块的类,所以没有数据类型提供转换,因此需要QVariant::value()函数或QVariantValue()模块函数。 (g)QVariant::type()函数返回存储在QVariant变量中的值的数据类型。
Qt常用的QVariant::type枚举类型变量:
变量 | 对应的类型 | 变量 | 对应的类型 |
---|
QVariant::Invalid | 无效类型 | QVariant::Time | QTime | QVariant::Region | QRegion | QVariant::Line | QLine | QVariant::Bitmap | QBitmap | QVariant::Palette | QPalette | QVariant::Bool | bool | QVariant::List | QList | QVariant::Brush | QBrush | QVariant::SizePolicy | QSizePolicy | QVariant::Size | QSize | QVariant::String | QString | QVariant::Char | QChar | QVariant::Map | QMap | QVariant::Color | QColor | QVariant::StringList | QStringList | QVariant::Cursor | QCursor | QVariant::Point | QPoint | QVariant::Date | QDate | QVariant::Pen | QPen | QVariant::DateTime | QDateTime | QVariant::Pixmap | QPixmap | QVariant::Double | double | QVariant::Rect | QRect | QVariant::Font | QFont | QVariant::Image | QImage | QVariant::Icon | QIcon | QVariant::UserType | 用户自定义类型 |
|