在面向对象的语言中,大多引入了容器的概念。那么 什么 是 容器?实质上就是一组相同类型对象的集合,但是它又不仅仅像数组那样简单,它实现了比数组更复杂的数据结构,当然也实现了比数组更强大的功能。

Qt库提供了一组通用的基于模板的容器类(container classes)。这些容器类可以用来存储指定类型的项目(items),例如,如果大家需要一个QString类型的可变大小的数组,那么可以使用QVector(QString)。与STL(Standard Template Library,C++的标准模板库)中的容器类相比,Qt中的这些容器类更轻量,更安全,更容易使用。

Qt容器类

Qt提供了一些顺序容器:QList,QLinkedList,QVector,QStack和QQueue。因为这些容器中的数据都是一个接一个线性存储的,所以称为顺序容器。对于大多数应用程序而言,使用最多的,而且最好用的是QList,虽然它是作为一个数组列表,但是它在头部和尾部进行添加操作是很快速的。如果需要使用一个链表,那么可以使用QLinkedList;如果希望数据项可以占用连续的内存空间,可以使用QVector。而QStack和QQueue作为便捷类,分别提供了后进先出(LIFO)和先进先出(FIFO)语义。

Qt还提供了一些关联容器:QMap,QMultiMap,QHash,QMultiHash和QSet。因为这些容器存储的是<键,值>对,比如QMap<Key,T>,所以称为关联容器。其中“Multi”容器用来方便支持一个键多个值的情况。

各容器类的定义及解释

49 QT容器类_容器类

49 QT容器类_操作符_02

QList

它是一个模板类,它提供了一个列表。QList<T>实际上是一个T类型项目的指针数组,所以它支持基于索引的访问,而且当项目的数目小于1000时,可以实现在列表中间进行快速的插入操作。QList提供了很多方便的接口函数来操作列表中的项目,例如:

  • 插入操作insert();
  • 替换操作replace();
  • 移除操作removeAt();
  • 移动操作move();
  • 交换操作swap();
  • 在表尾添加项目append();
  • 在表头添加项目prepend();
  • 移除第一个项目removeFirst();
  • 移除最后一个项目removeLast();
  • 从列表中移除一项并获取这个项目takeAt(),
  • 还有相应的takeFirst()和takeLast();
  • 获取一个项目的索引indexOf();
  • 判断是否含有相应的项目contains();
  • 获取一个项目出现的次数count()。

对于QList,可以使用“<<”操作符来向列表中插入项目,也可以使用“[ ]”操作符通过索引来访问一个项目,其中项目是从0开始编号的。不过,对于只读的访问,另一种方法是使用at()函数,它比“[ ]”操作符要快很多。

例如: 

QList<QString> list;
list << "aa" << "bb" << "cc"; // 插入项目
if(list[1] == "bb") list[1] = "ab";
list.replace(2,"bc"); // 将“cc”换为“bc”
qDebug() << "the list is: "; // 输出整个列表
for(int i=0; i<list.size(); ++i){
qDebug() << list.at(i); // 现在列表为aa ab bc
}
list.append("dd");// 在列表尾部添加
list.prepend("mm"); // 在列表头部添加
QString str = list.takeAt(2); // 从列表中删除第3个项目,并获取它
qDebug() << "at(2) item is: " << str;
qDebug() << "the list is: ";
for(int i=0; i<list.size(); ++i)
{
qDebug() << list.at(i); // 现在列表为mm aa bc dd
}

QMap

它是一个容器类,它提供了一个基于跳跃列表的字典(a skip-list-based dictionary)。QMap<Key,T>是Qt的通用容器类之一,它存储(键,值)对并提供了与键相关的值的快速查找。QMap中提供了很多方便的接口函数,例如:

  • 插入操作insert();
  • 获取值value();
  • 是否包含一个键contains();
  • 删除一个键remove();
  • 删除一个键并获取该键对应的值take();
  • 清空操作clear();
  • 插入一键多值insertMulti()。

可以使用“[ ]”操作符插入一个键值对或者获取一个键的值,不过当使用该操作符获取一个不存在的键的值时,会默认向map中插入该键,为了避免这个情况,可以使用value()函数来获取键的值。当使用value()函数时,如果指定的键不存在,那么默认会返回0,可以在使用该函数时提供参数来更改这个默认返回的值。QMap默认是一个键对应一个值的,但是也可以使用insertMulti()进行一键多值的插入,对于一键多值的情况,更方便的是使用QMap的子类QMultiMap。

例如:

QMap<QString,int> map;
map["one"] = 1; // 向map中插入("one",1)
map["three"] = 3;
map.insert("seven",7); // 使用insert()函数进行插入

// 获取键的值,使用“[ ]”操作符时,如果map中没有该键,那么会自动插入
int value1 = map["six"];
qDebug() << "value1:" << value1;
qDebug() << "contains 'six' ?" << map.contains("six");

// 使用value()函数获取键的值,这样当键不存在时不会自动插入
int value2 = map.value("five");
qDebug() << "value2:" << value2;
qDebug() << "contains 'five' ?" << map.contains("five");

// 当键不存在时,value()默认返回0,这里可以设定该值,比如这里设置为9
int value3 = map.value("nine",9);
qDebug() << "value3:" << value3;

嵌套和赋值

容器也可以嵌套使用,例如QMap<QString,QList<int> >,这里键的类型是QString,而值的类型是QList<int>,需要注意,在后面的“> >”符号之间要有一个空格,不然编译器会将它当做“>>”操作符对待。

在各种容器中所存储的值的类型可以是任何的可赋值的数据类型,该类型需要有一个默认的构造函数,一个拷贝构造函数和一个赋值操作运算符,像基本的类型double,指针类型,Qt的数据类型如QString、QDate、QTime等。但是QObject以及QObject的子类都不能存储在容器中,不过,可以存储这些类的指针,例如QList<QWidget*>。