Qt有和C++ STL类似的容器类。这一部分介绍Qt提供的容器和迭代器。
Qt的容器类也是模板类,可以包含任何其他可变类。包含链表,栈,队列maps和哈希表等一系列不同的容器。伴随这些类而来的迭代器有与stl兼容的迭代器和受java启发的Qt版本。迭代器是轻量级对象,用于在容器中移动并访问保存在容器中的数据
Tips: 所有Qt容器类都是隐式共享的,所以在容器被修改之前不会复制它(即copy on write)。将列表作为参数传递或将列表作为结果返回都是低性能和内存明智的选择。将const引用作为参数或结果传递给列表开销更低,因为它保证不会无意中做出任何更改。
1 Iterating QList
![](/wp-content/uploads/replace/862d394351efc2bdecd4b29b5b1eece1.jpeg)
填充QList<QString>和遍历打印其中的字符串内容
使用foreach宏可以缩短代码,但是迭代器实例是在幕后使用的。
Qt同时提供了stl风格的迭代器和java风格的迭代器。
![](/wp-content/uploads/replace/be0460135cbeb1f58fc8ca1930ff6422.jpeg)
STL-style迭代器和java-style风格迭代器
在比较STL风格和java风格的迭代器时,一定要记住,STL风格的迭代器效率更高一些。但是,java风格的迭代器提供的可读性和代码清晰性也提供了足够的使用他们的动机。
Tips: 通常使用typedef避免输入QList<>::Iterator. 例如 typedef QList<MyClass> MyClassList, 其迭代器typedef QList<MyClass>::Iterator MyClassListIterator。这样可以使STL风格的迭代器更具可读性。C++11标准下使用using为类型设置别名,即using MyClassList = QList<MyClass>,其迭代器别名using MyClassListIterator = QList<MyClass>::Iterator。
对于遍历时要修改容器内容的情况,STL风格和java风格的迭代器用法如下:
![](/wp-content/uploads/replace/c14554170b30d85f0b265f853af78aa9.jpeg)
使用迭代器修改list
Caution 当通过删除或插入项来修改列表时,迭代器可能会失效。在修改实际的列表(而不是列表的内容)时要注意这一点。
对于stl风格的迭代器,还可以使用–操作符和++操作符。对于java风格的迭代器,可以使用next、previous、findNext和findPrevious方法。当使用next和previous时,通过使用hasNext和hasPrevious来保护代码以避免未定义的结果是很重要的。
Tips: 当选择要使用的迭代器时,尽可能使用常量迭代器,因为它们可以提供更快的代码,并防止您错误地修改列表项
当您需要以专门化的方式进行迭代或只想访问特定的项时,您总是可以使用[ ]操作符或at方法进行索引访问。对于QList,这个过程非常快。
2 Filling the List
![](/wp-content/uploads/replace/f7f439a6df98af044e5e0de6a7cda86a.jpeg)
Appending Prepending and Inserting
对应以上操作,各个元素在链表中的顺序
![](/wp-content/uploads/replace/2293a678143c04a6e1a286fa69cf58d3.jpeg)
3 More Lists
大部分场景我们使用QList就足够了,使用QList的唯一缺点是,当您在大型列表中插入项时,它会变得非常慢。其他两个list类更为专门化,但不应将它们视为特殊情况。第一个是QVector类,它保证所包含的项在内存中按顺序排列,因此当您在列表的开始处和中间插入项时,必须移动列表中后面的所有项。其优点是索引访问和迭代非常快。第二种选择是QLinkedList,它提供了一个链表实现,提供快速迭代,但没有索引访问。它还支持与新项插入列表中的位置无关的常量时间插入。另一个好的方面是,只要元素留在列表中,迭代器就保持有效,可以在列表中自由地删除和插入新项,并且仍然使用迭代器。
![](/wp-content/uploads/replace/d6cbef49697768d105016f31c1d81e71.jpeg)
QList QVector QLinkedList的对比
4 Special Lists — QStringList
![](/wp-content/uploads/replace/85b285b301eabddd08126ad36915ba5c.jpeg)
QStringList继承自QList<QString>。它有一些特定于字符串的方法,这使得它非常有用。具体有哪些可以查阅文档。
5 Stacks 和 Queues
栈,后进先出(LIFO) ,队列先进先出(FIFO)。这个地方没什么好讲的,直接看例子。
![](/wp-content/uploads/replace/a6bfffbfda218cc4a8c7b89f3109a512.jpeg)
![](/wp-content/uploads/replace/c498bc097dfd1f25c20f6f2b4215ac75.jpeg)
6 Mapping 和 Hashing
不多解释,直接上例子
![](/wp-content/uploads/replace/08a41dd414887aba758c1e573eced034.jpeg)
创建QMap,关联字符串和整数
![](/wp-content/uploads/replace/f8444568d32c226d972a1791ec14b05e.jpeg)
控制台打印所有的key-value
![](/wp-content/uploads/replace/11d52083349aaff9caff3f511093fc22.jpeg)
遍历所有键-值对
在创建映射时,用作键的类型必须包含操作符==和<之所以这样定义,是因为映射必须能够比较键并对它们排序。QMap提供了良好的查找性能,因为它始终保持键的排序。如果这对您的应用程序不重要,那么您可以通过使用QHash来获得更高的性能。
QHash类的使用方式与QMap相同,但是键的顺序是任意的。哈希中用于键的类型必须具有==操作符和一个名为qHash的全局函数。qHash函数应该返回一个称为哈希键的无符号整数,用于在哈希列表中查找项。该函数的唯一要求是,对于相同的数据,它应该总是返回相同的值。Qt为最常见的类型提供了这样的函数,但是如果希望将自己的类放入散列表中,则必须提供这样的函数。哈希表的性能取决于它所期望的碰撞数,也就是说,产生相同散列键的键数。通过使用可能出现的键的知识,可以使用散列函数来提高性能。例如,在电话本应用程序中,人员可能具有相同的姓名,但通常不共享姓名和电话号码。
![](/wp-content/uploads/replace/db5f1fdc69de3d9bb56e65657ca4cf3e.jpeg)
A class holding name and number
对于这个类,必须提供一个==操作符和一个qHash函数。操作符确保名称和数字都匹配。qHash函数从qHash(QString)函数获取名称和数字的哈希值,并使用XOR逻辑运算符(^)将它们连接起来。
![](/wp-content/uploads/replace/12a1f8f8b3f759fe0cfef25bf888b82a.jpeg)
Hash function for the Person class
![](/wp-content/uploads/replace/b43c0ece23a0177837c60caa370c6b01.jpeg)
Hashing the Person class
有时感兴趣不是将值映射到键,而是记住哪些键是有效的。在这种情况下,您可以使用QSet类。一个集合是一个没有 值的 散列,因此必须有一个qHash函数和一个==操作符来表示键。而且,键的顺序是任意的。
![](/wp-content/uploads/replace/edbc23933dc1390d3f2f7505070caa81.jpeg)