QList 是 Qt 中一个非常重要且常用的序列容器类,它提供了动态数组的功能,支持快速的基于索引的访问以及高效的插入和删除操作。
表格速览
下面是一个汇总了 QList 常用方法的表格:
方法类别 | 方法名称 | 功能描述 | 示例 |
---|---|---|---|
构造函数 | QList() |
创建一个空的 QList。 | QList<int> list; |
QList(std::initializer_list<T> args) |
使用初始化列表创建 QList。 | QList<int> list = {1, 2, 3}; |
|
添加元素 | append(const T &value) |
在列表末尾添加一个元素。 | list.append(4); |
prepend(const T &value) |
在列表开头添加一个元素。 | list.prepend(0); |
|
insert(int i, const T &value) |
在索引位置 i 处插入一个元素。 | list.insert(1, 5); // 在索引1处插入5 |
|
operator<<(const T &value) |
使用 << 操作符在列表末尾添加元素(支持链式调用)。 |
list << 6 << 7; |
|
访问元素 | at(int i) const |
返回索引位置 i 处的元素(只读,常推荐使用)。 | int value = list.at(0); |
operator[](int i) |
返回索引位置 i 处的元素(可读写)。 | list[0] = 10; |
|
first() |
返回第一个元素。 | int first = list.first(); |
|
last() |
返回最后一个元素。 | int last = list.last(); |
|
value(int i, const T &defaultValue) |
返回索引位置 i 处的元素,如果索引无效则返回默认值 defaultValue。 | int val = list.value(10, -1); // 如果索引10无效则返回-1 |
|
删除元素 | removeAt(int i) |
删除索引位置 i 处的元素。 | list.removeAt(0); |
removeFirst() |
删除第一个元素。 | list.removeFirst(); |
|
removeLast() |
删除最后一个元素。 | list.removeLast(); |
|
removeOne(const T &value) |
删除列表中第一个出现的 value。 | list.removeOne(5); |
|
removeAll(const T &value) |
删除列表中所有出现的 value,并返回删除的个数。 | int count = list.removeAll(5); |
|
takeAt(int i) |
删除索引位置 i 处的元素并返回该元素。 | int taken = list.takeAt(0); |
|
takeFirst() |
删除并返回第一个元素。 | int first = list.takeFirst(); |
|
takeLast() |
删除并返回最后一个元素。 | int last = list.takeLast(); |
|
clear() |
清空列表中的所有元素。 | list.clear(); |
|
容量查询 | size() const |
返回列表中元素的个数。 | int count = list.size(); |
isEmpty() const |
判断列表是否为空。 | if (list.isEmpty()) { /* ... */ } |
|
count(const T &value) const |
返回列表中 value 出现的次数。 | int num = list.count(5); |
|
查找元素 | indexOf(const T &value, int from = 0) |
从索引 from 开始向后查找 value 第一次出现的索引位置,未找到返回 -1。 | int index = list.indexOf(5); |
lastIndexOf(const T &value, int from=-1) |
从索引 from 开始向前查找 value 最后一次出现的索引位置,未找到返回 -1。 | int index = list.lastIndexOf(5); |
|
contains(const T &value) const |
判断列表是否包含 value。 | if (list.contains(5)) { /* ... */ } |
|
移动交换 | move(int from, int to) |
将元素从索引 from 处移动到索引 to 处。 | list.move(0, 2); // 将第一个元素移动到第三个位置 |
swap(int i, int j) |
交换索引 i 和索引 j 处的元素。 | list.swap(0, 1); // 交换第一个和第二个元素 |
|
swapItemsAt(int i, int j) |
交换索引 i 和索引 j 处的元素(Qt 5.13 引入)。 | list.swapItemsAt(0, 1); |
|
迭代器 | begin() / end() |
返回指向列表开头和末尾(最后一个元素之后)的读写迭代器。 | for (auto it = list.begin(); it != list.end(); ++it) { *it += 1; } |
constBegin() / constEnd() |
返回指向列表开头和末尾(最后一个元素之后)的只读迭代器。 | for (auto it = list.constBegin(); it != list.constEnd(); ++it) { qDebug() << *it; } |
|
转换 | toVector() const |
将 QList 转换为 QVector。 | QVector<int> vec = list.toVector(); |
toStdList() const |
将 QList 转换为 std::list。 | std::list<int> stdList = list.toStdList(); |
|
fromVector(const QVector<T> &vector) |
将 QVector 转换为 QList(静态方法)。 | QList<int> list = QList<int>::fromVector(vec); |
|
fromStdList(const std::list<T> &list) |
将 std::list 转换为 QList(静态方法)。 | QList<int> list = QList<int>::fromStdList(stdList); |
一、概述
QList 是 Qt 中最常用的容器类之一,它是一个模板类,提供了动态数组的功能。QList 不是链表,与 STL 中的 std::vector 类似,但是是优化过的vector,官方的形容是array list,具有 Qt 特有的优势,如隐式共享和 Qt 风格的 API。
核心特性
- 动态数组:自动管理内存,可动态调整大小
- 隐式共享:写时复制机制,提高性能
- 快速插入:在列表两端插入元素非常高效
- 类型安全:模板类提供编译时类型检查
二、基本操作
下面通过一些代码片段来展示 QList 的基本用法。
创建和初始化
QList 支持多种初始化方式:
#include <QList>
#include <QDebug>
#include <QString>
// 创建一个空的 QList
QList<int> intList;
QList<QString> stringList;
// 使用初始化列表创建 (C++11)
QList<int> numbers = {1, 2, 3, 4, 5};
QList<QString> names = {"Alice", "Bob", "Charlie"};
// 指定初始大小
QList<double> values(10); // 创建包含10个默认构造元素的列表
添加元素
QList<int> list;
// 使用 << 操作符添加元素(这很Qt)
list1 << 10 << 20 << 30;
// 使用 append 在某尾添加元素
list.append(40);
list.append(50);
// 使用 prepend 在头部添加元素
list.prepend(0);
// 使用 insert 在指定位置插入元素
list.insert(2, 15); // 在索引2(第三个位置)插入15
访问元素
QList<QString> list = {"A", "B", "C", "D"};
// 使用 at() 函数(只读,推荐使用,因为速度快且安全,编译器会边界检查)
QString third = list.at(2);
// 使用 operator[](可读写)
qDebug() << "Element at index 3:" << list[3]; // 输出 D
list[3] = "E"; // 修改索引3处的元素为"E"
// 获取第一个和最后一个元素
qDebug() << "First element:" << list.first(); // 输出 "A"
qDebug() << "Last element:" << list.last(); // 输出 "E"
// 使用 value() 函数,如果索引无效可返回默认值
qDebug() << "Value at index 100:" << list.value(100, -1); // 索引100无效,输出 -1
删除元素
根据不同的需求删除元素:
QList<int> numList = {10, 20, 30, 40, 50, 20, 30};
// 删除指定索引的元素{10, 20, 30, 40, 50, 20, 30}
numList.removeAt(0); // 删除索引0的元素 (10)
// 删除第一个元素 {20, 30, 40, 50, 20, 30}
numList.removeFirst(); // 删除第一个元素 (20)
// 删除最后一个元素 {30, 40, 50, 20, 30}
numList.removeLast(); // 删除最后一个元素 (30)
// 删除第一个匹配到的值 {30, 40, 50, 20}
numList.removeOne(20); // 删除第一个遇到的20
// 删除所有匹配到的值,返回删除的个数 {30, 40, 50}
int removedCount = numList.removeAll(30); // 删除所有的30,返回1
// 取出并删除指定位置的元素 {40,50}
int takenValue = numList.takeAt(1); // 取出并删除索引1的元素 (40),numList 还剩 [50]
// 清空整个列表
numList.clear(); // 列表变为空
三、查询与遍历
查找元素
QList 提供了方便的查找功能:
QList<QString> fruitList = {"apple", "banana", "orange", "apple", "grape"};
// 获取列表大小
int count = fruitList.size(); // 3
int count2 = fruitList.count(); // 3 - 与size()相同
bool isEmpty = fruitList.isEmpty(); // false
// 查找元素第一次出现的索引,找不到返回 -1
int firstIndex = fruitList.indexOf("apple"); // 返回 0
int nextIndex = fruitList.indexOf("apple", firstIndex + 1); // 从索引1开始找,返回 3
// 查找最后一次出现的索引
int lastIndex = fruitList.lastIndexOf("apple"); // 返回 3
// 检查是否包含某个元素
bool hasOrange = fruitList.contains("orange"); // 返回 true
// 统计某个元素出现的次数
int appleCount = fruitList.count("apple"); // 返回 2
遍历元素
遍历 QList 有多种方法:
QList<int> list = {1, 2, 3, 4, 5};
// 1. 使用索引循环 (适合需要索引号时)
for (int i = 0; i < list.size(); ++i) {
qDebug() << "Index" << i << ":" << list.at(i);
}
// 2. 使用 C++11 范围循环 (只读,简洁,推荐)
for (const int &num : list) {
qDebug() << "Number:" << num;
}
// 3. 使用 STL 风格的迭代器 (读写)
for (QList<int>::iterator it = list.begin(); it != list.end(); ++it) {
*it += 1; // 每个元素加1
qDebug() << "Iterator:" << *it;
}
// 4. 使用 STL 风格的只读(常量)迭代器
for (QList<int>::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) {
qDebug() << "Const Iterator:" << *it;
}
// 5.1 使用 Java 风格的迭代器 (Qt特有,略旧)
QListIterator<int> javaIterator(list);
while (javaIterator.hasNext()) {
qDebug() << "Java Style:" << javaIterator.next();
}
// 方法5.2: 使用Qt的foreach宏(已过时,但仍可用)
foreach (int value, list) {
qDebug() << value;
}
四、高级操作
列表操作
QList<int> list1 = {1, 2, 3};
QList<int> list2 = {4, 5, 6};
// 合并列表
QList<int> combined = list1 + list2; // {1, 2, 3, 4, 5, 6}
// 追加列表
list1.append(list2); // list1变为 {1, 2, 3, 4, 5, 6}
// 提取子列表
QList<int> subList = list1.mid(1, 3); // 从索引1开始,取3个元素: {2, 3, 4}
// 移动元素
list1.move(0, 2); // 将索引0的元素移动到索引2
排序和其他操作
QList 可以方便地与其他算法结合使用:
QList<int> list = {5, 3, 8, 1, 2};
// 排序(升序),jianrong
std::sort(list.begin(), list.end()); // list becomes {1, 2, 3, 5, 8}
// 使用Qt的qSort(较老,推荐使用std::sort)
qSort(list); // 升序排序
// 自定义排序(降序)
std::sort(list.begin(), list.end(), [](int a, int b) { return a > b; }); // list becomes {8, 5, 3, 2, 1}
std::sort(list.begin(), list.end(), std::greater<int>()); // 同样效果{8, 5, 3, 2, 1}
// 过滤列表(使用STL算法)
QList<int> evenNumbers;
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(evenNumbers),
[](int n) { return n % 2 == 0; }); // 只保留偶数
// 移动元素
list.move(0, 2); // 将索引0的元素 (8) 移动到索引2的位置,列表变为 {5, 3, 8, 2, 1} (假设原列表是 {8, 5, 3, 2, 1})
// 交换元素
list.swap(1, 3); // 交换索引1和索引3的元素,列表变为 {5, 2, 8, 3, 1}
list.swapItemsAt(0, 4); // 交换索引0和索引4的元素,列表变为 {1, 2, 8, 3, 5} (Qt 5.13引入)
五、QList 及其子类
QList 与 QStringList
// QStringList 是 QList<QString> 的别名
QStringList strings = {"Hello", "World", "Qt"};
// QStringList 特有的方法
strings.sort(); // 排序
strings.join(", "); // 连接为字符串: "Hello, World, Qt"
strings.filter("o"); // 过滤包含"o"的字符串: ["Hello", "World"]
// 分割字符串创建列表
QString text = "apple,banana,cherry";
QStringList fruits = text.split(","); // ["apple", "banana", "cherry"]
六、QList与其他容器
QList vs QVector
特性 | QList | QVector |
---|---|---|
内存布局 | 可能不是连续内存 | 连续内存 |
中间插入 | 相对较快 | 相对较慢 |
随机访问 | 快速 | 非常快速 |
适用场景 | 通用目的,频繁插入删除 | 需要连续内存或与C API交互 |
注意: 在Qt 6中,QVector只是QList的别名,两者实现相同。
QList vs std::vector
特性 | QList | std::vector |
---|---|---|
内存管理 | 隐式共享 | 无隐式共享 |
API风格 | Qt风格 | STL风格 |
与其他Qt类集成 | 优秀 | 需要转换 |
性能特性 | 优化了前置插入 | 优化了随机访问 |
实践建议
- 元素类型要求:QList 的模板类型
T
必须是可赋值的数据类型 (Assignable Data Type)。大多数基本数据类型(如int
,double
,QString
等)和 Qt 数据类型都满足此要求。
但像QWidget
这样的对象不能直接存储为值,可以存储其指针(如QList<QWidget*>
)。某些成员函数(如indexOf()
,contains()
,removeAll()
)还要求类型T
支持operator==()
。 - 自我引用:QList 不支持插入、前置、附加或替换对自身值的引用。这样做会导致应用程序中止并报错。
- 索引有效性:QList 的成员函数通常不会验证输入参数的有效性(除了
isEmpty()
)。传递无效的索引(例如超出范围的索引)可能导致未定义行为或崩溃。因此,在使用基于索引的函数(如at(i)
,operator[](i)
,removeAt(i)
等)之前,最好使用isEmpty()
检查列表是否为空,并确保索引在有效范围内(0 <= index < size()
)。 - 性能考量:
- QList, QVector, QLinkedList 的选择:
- QVector 通常是存储大量元素或需要频繁在尾部添加/删除元素的默认首选,因为它在内存中连续存储元素,一次分配可能分配多个元素的空间。
- QList 在许多 Qt API 中被广泛使用,并且在大多数情况下性能良好。它在内部根据元素大小和类型采用不同的优化策略(如果
sizeof(T) <= sizeof(void*)
且T
是Q_MOVABLE_TYPE
或Q_PRIMITIVE_TYPE
,则直接在数组中存储值;否则存储指针)。 - QLinkedList 在频繁进行大量中间插入和删除操作时可能效率更高,但它不支持基于索引的随机访问。
- 迭代器失效:在进行某些修改操作(如插入、删除)后,指向 QList 的迭代器、指针和引用可能会失效,需要谨慎使用。
- QList, QVector, QLinkedList 的选择:
代码片段
内存管理
// QList使用隐式共享,多个列表可以共享数据
QList<int> list1 = {1, 2, 3};
QList<int> list2 = list1; // 此时共享数据
list2[0] = 99; // 写时复制发生,list1和list2现在有独立的数据
// 明确分离数据(如果需要修改且不想影响其他副本)
list2.detach();
预分配内存
QList<int> bigList;
// 预分配内存以提高性能
bigList.reserve(1000); // 预分配1000个元素的空间
for (int i = 0; i < 1000; ++i) {
bigList.append(i); // 不会频繁重新分配内存
}
// 压缩内存使用(释放多余容量)
bigList.squeeze();
正确使用 const 引用
// 避免不必要的拷贝:使用const引用传递QList
void processList(const QList<int> &list) {
// 只读访问,不会发生拷贝
for (int value : list) {
// 处理数据
}
}
// 如果需要修改,传递非const引用
void modifyList(QList<int> &list) {
list.append(42);
}
应用场景
1. 数据存储和处理
// 存储自定义对象
class Person {
public:
QString name;
int age;
};
QList<Person> people;
people.append({"Alice", 30});
people.append({"Bob", 25});
// 使用算法处理
std::sort(people.begin(), people.end(),
[](const Person &a, const Person &b) { return a.age < b.age; });
2. 作为函数返回值
// 返回QList(受益于隐式共享,高效)
QList<int> generateNumbers(int count) {
QList<int> result;
for (int i = 0; i < count; ++i) {
result.append(i * i);
}
return result; // 不会发生深拷贝
}
3. 与Qt其他类配合使用
// 与QComboBox配合
QComboBox *comboBox = new QComboBox;
QStringList items = {"Option 1", "Option 2", "Option 3"};
comboBox->addItems(items);
// 与QListView配合
QListView *listView = new QListView;
QStringListModel *model = new QStringListModel(items);
listView->setModel(model);
Tips
迭代器失效:在修改列表时,迭代器可能会失效
QList<int> list = {1, 2, 3, 4, 5}; for (auto it = list.begin(); it != list.end(); ++it) { if (*it == 3) { list.erase(it); // 删除元素后,迭代器失效 break; // 必须立即退出或重新获取迭代器 } }
类型安全:QList是类型安全的,但要避免不安全的转换
QList<QString> stringList = {"1", "2", "3"}; // 错误:不能直接转换为int列表 // QList<int> intList = stringList; // 正确:显式转换 QList<int> intList; for (const QString &str : stringList) { intList.append(str.toInt()); }
性能考虑:对于大量数据,考虑使用更合适的容器
// 如果需要频繁在中间插入删除,考虑QLinkedList(在Qt 5中) // 如果需要连续内存,考虑QVector(在Qt 5中)或std::vector
持续学习更新中。。。。。。。