在 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: 3return 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: 1return 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 容器,可以提高代码的性能和可读性。