在 Qt 中,容器类是用于存储和管理数据的核心工具。Qt 提供了一系列高效的容器类,这些容器类设计用于 C++ 编程,并且与 Qt 的信号与槽机制、隐式共享(Implicit Sharing)等特性紧密集成。Qt 的容器类比标准库的 std::vector
、std::list
等容器更加轻量级,并且在某些情况下性能更好。
1. Qt 容器的特点
- 隐式共享:Qt 容器是隐式共享的,这意味着在复制容器时,只有当修改数据时才会真正复制,从而提高了性能。
- 线程安全:Qt 容器在单线程环境下是线程安全的,但在多线程环境下需要手动同步。
- 与 STL 兼容:Qt 容器提供了与标准库容器的兼容接口,可以与 STL 算法一起使用。
- 轻量级:Qt 容器的内存占用通常比标准库容器更小。
2. Qt 容器类概览
Qt 提供了多种容器类,适用于不同的场景:
容器类 | 描述 | 适用场景 |
---|---|---|
QList<T> |
动态数组,支持快速随机访问和快速插入/删除。 | 常用容器,适合大多数场景。 |
QLinkedList<T> |
双向链表,支持快速插入/删除,但不支持随机访问。 | 需要频繁插入/删除操作的场景。 |
QVector<T> |
动态数组,支持快速随机访问和快速尾部插入。 | 需要高效随机访问的场景。 |
QSet<T> |
集合,存储唯一元素,支持快速查找。 | 需要存储唯一元素的场景。 |
QMap<Key, T> |
有序映射,按键排序存储键值对。 | 需要按键排序的场景。 |
QMultiMap<Key, T> |
多值映射,支持一个键对应多个值。 | 需要一个键对应多个值的场景。 |
QHash<Key, T> |
无序映射,支持快速查找。 | 需要快速查找的场景。 |
QMultiHash<Key, T> |
多值哈希,支持一个键对应多个值。 | 需要一个键对应多个值的场景。 |
QStack<T> |
栈,后进先出(LIFO)。 | 需要栈结构的场景。 |
QQueue<T> |
队列,先进先出(FIFO)。 | 需要队列结构的场景。 |
3. 常用容器详解
3.1 QList<T>
QList
是 Qt 中最常用的容器类,类似于 std::vector
,但具有更高的性能。
#include <QList>
#include <QDebug>
int main() {
QList<int> list;
list << 1 << 2 << 3; // 添加元素
qDebug() << "List:" << list; // 输出: List: (1, 2, 3)
list.append(4); // 追加元素
list.prepend(0); // 在头部插入元素
qDebug() << "List after modification:" << list; // 输出: List: (0, 1, 2, 3, 4)
return 0;
}
3.2 QLinkedList<T>
QLinkedList
是双向链表,适合频繁插入和删除操作。
#include <QLinkedList>
#include <QDebug>
int main() {
QLinkedList<int> list;
list << 1 << 2 << 3;
qDebug() << "LinkedList:" << list; // 输出: LinkedList: (1, 2, 3)
list.append(4);
list.prepend(0);
qDebug() << "LinkedList after modification:" << list; // 输出: LinkedList: (0, 1, 2, 3, 4)
return 0;
}
3.3 QVector<T>
QVector
类似于 QList
,但更适合需要高效随机访问的场景。
#include <QVector>
#include <QDebug>
int main() {
QVector<int> vector;
vector << 1 << 2 << 3;
qDebug() << "Vector:" << vector; // 输出: Vector: (1, 2, 3)
vector.append(4);
vector.prepend(0);
qDebug() << "Vector after modification:" << vector; // 输出: Vector: (0, 1, 2, 3, 4)
return 0;
}
3.4 QSet<T>
QSet
是集合,存储唯一元素,支持快速查找。
#include <QSet>
#include <QDebug>
int main() {
QSet<int> set;
set << 1 << 2 << 3 << 2 << 1; // 重复元素会被忽略
qDebug() << "Set:" << set; // 输出: Set: {1, 2, 3}
set.insert(4);
qDebug() << "Set after modification:" << set; // 输出: Set: {1, 2, 3, 4}
return 0;
}
3.5 QMap<Key, T>
QMap
是有序映射,按键排序存储键值对。
#include <QMap>
#include <QDebug>
int main() {
QMap<QString, int> map;
map.insert("Alice", 25);
map.insert("Bob", 30);
map.insert("Charlie", 35);
qDebug() << "Map:" << map; // 输出: Map: {("Alice", 25), ("Bob", 30), ("Charlie", 35)}
map.insert("David", 40);
qDebug() << "Map after modification:" << map; // 输出: Map: {("Alice", 25), ("Bob", 30), ("Charlie", 35), ("David", 40)}
return 0;
}
3.6 QHash<Key, T>
QHash
是无序映射,支持快速查找。
#include <QHash>
#include <QDebug>
int main() {
QHash<QString, int> hash;
hash.insert("Alice", 25);
hash.insert("Bob", 30);
hash.insert("Charlie", 35);
qDebug() << "Hash:" << hash; // 输出: Hash: {("Charlie", 35), ("Bob", 30), ("Alice", 25)}
hash.insert("David", 40);
qDebug() << "Hash after modification:" << hash; // 输出: Hash: {("Charlie", 35), ("Bob", 30), ("Alice", 25), ("David", 40)}
return 0;
}
3.7 QStack<T>
QStack
是栈,后进先出(LIFO)。
#include <QStack>
#include <QDebug>
int main() {
QStack<int> stack;
stack.push(1);
stack.push(2);
stack.push(3);
qDebug() << "Stack:" << stack; // 输出: Stack: QStack(3, 2, 1)
int top = stack.pop(); // 弹出栈顶元素
qDebug() << "Popped element:" << top; // 输出: Popped element: 3
return 0;
}
3.8 QQueue<T>
QQueue
是队列,先进先出(FIFO)。
#include <QQueue>
#include <QDebug>
int main() {
QQueue<int> queue;
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
qDebug() << "Queue:" << queue; // 输出: Queue: QQueue(1, 2, 3)
int front = queue.dequeue(); // 移除队列头部元素
qDebug() << "Dequeued element:" << front; // 输出: Dequeued element: 1
return 0;
}
4. Qt 容器的迭代器
Qt 容器提供了多种迭代器,用于遍历容器中的元素。迭代器分为两种类型:
- 只读迭代器:用于遍历容器中的元素,但不能修改元素。
- 读写迭代器:用于遍历和修改容器中的元素。
4.1 只读迭代器
#include <QList>
#include <QDebug>
int main() {
QList<int> list = {1, 2, 3, 4, 5};
// 使用只读迭代器遍历
QList<int>::const_iterator it;
for (it = list.constBegin(); it != list.constEnd(); ++it) {
qDebug() << *it; // 输出: 1 2 3 4 5
}
return 0;
}
4.2 读写迭代器
#include <QList>
#include <QDebug>
int main() {
QList<int> list = {1, 2, 3, 4, 5};
// 使用读写迭代器遍历并修改元素
QList<int>::iterator it;
for (it = list.begin(); it != list.end(); ++it) {
*it *= 2; // 将每个元素乘以 2
}
qDebug() << "Modified list:" << list; // 输出: Modified list: (2, 4, 6, 8, 10)
return 0;
}
5. Qt 容器的隐式共享
Qt 容器是隐式共享的,这意味着在复制容器时,只有当修改数据时才会真正复制,从而提高了性能。
示例:隐式共享
#include <QList>
#include <QDebug>
int main() {
QList<int> list1 = {1, 2, 3};
QList<int> list2 = list1; // 浅拷贝,共享数据
qDebug() << "list1:" << list1; // 输出: list1: (1, 2, 3)
qDebug() << "list2:" << list2; // 输出: list2: (1, 2, 3)
list2[0] = 10; // 修改 list2,触发深拷贝
qDebug() << "After modification:";
qDebug() << "list1:" << list1; // 输出: list1: (1, 2, 3)
qDebug() << "list2:" << list2; // 输出: list2: (10, 2, 3)
return 0;
}
6. Qt 容器与 STL 的兼容性
Qt 容器提供了与标准库容器的兼容接口,可以与 STL 算法一起使用。
示例:使用 STL 算法
#include <QList>
#include <QDebug>
#include <algorithm> // 包含 STL 算法
int main() {
QList<int> list = {3, 1, 4, 1, 5, 9};
// 使用 STL 的 sort 算法
std::sort(list.begin(), list.end());
qDebug() << "Sorted list:" << list; // 输出: Sorted list: (1, 1, 3, 4, 5, 9)
return 0;
}
7. Qt 容器的性能对比
容器类 | 随机访问 | 插入/删除 | 内存占用 | 适用场景 |
---|---|---|---|---|
QList<T> |
快 | 快 | 中等 | 常用容器,适合大多数场景。 |
QLinkedList<T> |
慢 | 快 | 高 | 需要频繁插入/删除操作的场景。 |
QVector<T> |
快 | 慢 | 低 | 需要高效随机访问的场景。 |
QSet<T> |
快 | 快 | 高 | 需要存储唯一元素的场景。 |
QMap<Key, T> |
快 | 慢 | 高 | 需要按键排序的场景。 |
QHash<Key, T> |
快 | 快 | 高 | 需要快速查找的场景。 |
8. 总结
Qt 提供了丰富的容器类,适用于不同的场景。以下是一些建议:
- 常用容器:
QList
是最常用的容器,适合大多数场景。 - 高效随机访问:
QVector
是更好的选择。 - 频繁插入/删除:
QLinkedList
是更好的选择。 - 唯一元素:
QSet
是更好的选择。 - 键值对存储:
QMap
和QHash
分别适用于有序和无序的场景。
通过合理选择和使用 Qt 容器,可以提高代码的性能和可读性。