Qt为我们提供了一系列基于模板的容器类,来存储特定类型的项。这些容器类都是隐式共享的;被用作只读容器时,是线程安全的。
与STL类似,也分为序列式容器和关联式容器。
序列式容器有:QVector、QList、QLinkList、QStack、QQueue。对大部分程序来说QList都是一个很好的选择,快速添加操作;如果你想要确保元素占用连续内存,可以用QVector;而QStack和QQueue分别提供了LIFO和FIFO操作。
关联式容器有:QMap、QMultiMap、QHash、QMultiHash、QSet。这些容器存储为Key-Value对,Multi表示支持一个Key关联多个Value;Hash容器通过hash函数而不是二分法查找,提供了更快速的查找操作;
(1)QVector:顺序表,最通用。这个容器类会在一块相邻的内存中存储一个给定类型的值的数组。在一个QVector的前端或中间插入是非常慢的,因为这会导致大量现存的元素移动以为新的元素腾出位置。
(2)QList:链表,最通用。这个容器类维护一个指针数组,指针指向存储的给定类型的值。有两种存储方式:
当元素占内存 <= 指针占内存时,且元素使用Q_DECLARE_TYPEINFO声明为Q_MOVABLE_TYPE或Q_PRIMITIVE_TYPE时,那么QList存储方式和QVector一样,都是数组形式;
当元素占内存 >指针占内存时,那么QList会把每一个元素new到堆上;
PS:Q_DECLARE_TYPEINFO(Type, Flags):可以使用此宏指定有关自定义类型的信息。有了准确的类型信息,Qt的通用容器可以选择合适的存储方法和算法。类型如下:
Q_PRIMITIVE_TYPE指定该类型是一个POD(纯旧数据)类型,没有构造函数或析构函数,或者是一个类型,其中每个位模式都是有效的对象,memcpy()创建该对象的有效独立副本;
Q_MOVABLE_TYPE指定该类型具有构造函数和/或析构函数,但可以使用memcpy()在内存中移动。注意:尽管有名称,但这与移动构造函数或C++移动语义无关;
Q_COMPLEX_TYPE(默认值)指定该类型具有构造函数和/或析构函数,并且不能在内存中移动它;
PS:当数据量比较小时(小于1000项),QVector和QList没区别;当需要开辟连续空间,或元素远比指针占内存大时,用QVector;当追加元素时,QVector大概快百分之5;当经常查找、按索引获取元素时,QList比较快;
(3)QLinkedList:类似QList,迭代器访问而不是下标。中间插入时比QList更快。有更好的迭代语义,即指向QLinkedList中某个元素的迭代器,只要该元素存在就会一直保持有效,而指向QList中某元素的迭代器,在向QList进行任意插入或删除时都会导致失效。
(4)QStack:继承自QVector,提供了“先入后出”的语义。
(5)QQueue:继承自QList,提供了“先入先出”的语义。
(6)QSet:提供了不允许有重复值的集合,提供快速的查找效率。与STL中set是有本质区别的,QSet基于哈希表,set基于红黑树变种。
(7)QMap
(8)QMultiMap
(9)QHash
(10)QMultiHash
综述:这些容器中存储的值可以是任何能被赋值的数据类型,即该类型必须提供一个默认的构造函数、一个拷贝构造函数、一个赋值运算符。这样的数据类型涵盖了大部分你可以存储的类型,包括基本类型int和double,指针类型,Qt的数据类型QString,QDate,QTime,但不包括QObject或其子类(QWidget,QDialog,QTimer等等)。如果你尝试构建一个QList类型的变量,编译器就会提示你QWidget类的拷贝构造函数和赋值操作符是被禁用的。如果你想存储这些类的对象,可以存储它们的指针类型,例如QList
QList 和 QVector 都是 Qt 框架中提供的动态数组类,它们有一些相似之处,但也有一些重要的区别。
相似之处:
都可以用来存储和操作一系列元素,类似于动态数组。
都提供了方便的方法来插入、删除、访问和修改元素。
都可以在任意位置插入和删除元素,而不会导致整个数组的重新分配和复制。
都支持迭代器用于遍历元素。
都可以使用索引访问元素。
区别:
内部实现:QList 是一个双向链表,而 QVector 是一个基于数组的容器。
QList 的内部结构使用链表连接元素,插入和删除元素的开销较低,但访问元素的开销较高。
QVector 内部使用连续的内存块存储元素,访问元素的开销较低,但插入和删除元素时需要进行内存重分配和数据复制的操作,因此开销较高。
动态调整大小:QList 在插入和删除元素时,不会导致整个数组的重新分配和复制,因此调整大小的开销相对较小。而 QVector 在插入和删除元素时,可能需要进行内存重分配和数据复制,以便保持连续的内存布局。
内存占用:由于 QList 使用链表结构,每个元素需要额外的指针来维护链表连接,因此在内存占用方面略微高于 QVector。
随机访问效率:由于 QVector 内部使用连续的内存块,可以通过索引进行快速随机访问,而 QList 需要遍历链表来访问指定位置的元素,因此 QVector 在随机访问的性能上更好。
迭代器的稳定性:QList 的迭代器在插入和删除元素后仍然有效,而 QVector 的迭代器在插入和删除元素后可能会失效。
基于以上区别,一般来说:
如果需要频繁的插入和删除操作,或者不关心随机访问的性能,可以选择 QList。
如果需要频繁的随机访问,而插入和删除操作相对较少,可以选择 QVector。
需要根据具体的使用场景和性能需求来选择合适的容器类。
QVector、QList、QSet、QMap 和 QQueue 是 Qt 框架中常用的容器类,它们在存储和组织数据上有一些区别。
QVector:
QVector 是一个基于数组的动态数组类,支持高效的随机访问。
可以在任意位置插入和删除元素,但在中间位置进行插入和删除操作时的性能较差。
适用于需要频繁随机访问元素的场景。
QList:
QList 是一个双向链表类,支持高效的插入和删除操作。
可以在任意位置插入和删除元素,而不会导致整个链表的重新分配和复制。
适用于需要频繁插入和删除元素的场景。
QSet:
QSet 是一个集合类,存储不重复的元素。
元素的顺序是不确定的,不支持索引访问。
使用哈希表实现,具有高效的插入、删除和查找操作。
适用于需要存储不重复元素并且需要高效的插入、删除和查找的场景。
QMap:
QMap 是一个关联容器类,存储键值对(key-value pairs)。
元素按照键的排序顺序存储,支持按键进行快速查找。
适用于需要按键进行快速查找和排序的场景。
QQueue:
QQueue 是一个队列类,遵循先进先出(FIFO)的原则。
只能在队列的末尾插入元素,在队列的开头删除元素。
适用于需要实现队列数据结构的场景。
需要注意的是,QVector、QList、QSet 和 QMap 都是可变容器,可以动态调整大小,并支持插入、删除和访问操作。它们的选择取决于具体的需求和使用场景。
另外,还有其他一些 Qt 容器类,如 QLinkedList(双向链表类)、QHash(哈希表类)、QStack(栈类)等,它们提供了不同的特性和适用性,可以根据具体情况选择合适的容器类。