Qt Core 之 QList

发布于:2025-09-05 ⋅ 阅读:(18) ⋅ 点赞:(0)
QList
QStringList
QQueue
QItemSelection
QSignalSpy
QTestEventList

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类集成 优秀 需要转换
性能特性 优化了前置插入 优化了随机访问

实践建议

  1. 元素类型要求:QList 的模板类型 T 必须是可赋值的数据类型 (Assignable Data Type)。大多数基本数据类型(如 int, double, QString 等)和 Qt 数据类型都满足此要求。
    但像 QWidget 这样的对象不能直接存储为值,可以存储其指针(如 QList<QWidget*>)。某些成员函数(如 indexOf(), contains(), removeAll())还要求类型 T 支持 operator==()
  2. 自我引用:QList 不支持插入、前置、附加或替换对自身值的引用。这样做会导致应用程序中止并报错。
  3. 索引有效性:QList 的成员函数通常不会验证输入参数的有效性(除了 isEmpty())。传递无效的索引(例如超出范围的索引)可能导致未定义行为或崩溃。因此,在使用基于索引的函数(如 at(i), operator[](i), removeAt(i) 等)之前,最好使用 isEmpty() 检查列表是否为空,并确保索引在有效范围内(0 <= index < size())。
  4. 性能考量
    • QList, QVector, QLinkedList 的选择
      • QVector 通常是存储大量元素或需要频繁在尾部添加/删除元素的默认首选,因为它在内存中连续存储元素,一次分配可能分配多个元素的空间。
      • QList 在许多 Qt API 中被广泛使用,并且在大多数情况下性能良好。它在内部根据元素大小和类型采用不同的优化策略(如果 sizeof(T) <= sizeof(void*)TQ_MOVABLE_TYPEQ_PRIMITIVE_TYPE,则直接在数组中存储值;否则存储指针)。
      • QLinkedList 在频繁进行大量中间插入和删除操作时可能效率更高,但它不支持基于索引的随机访问。
    • 迭代器失效:在进行某些修改操作(如插入、删除)后,指向 QList 的迭代器、指针和引用可能会失效,需要谨慎使用。

代码片段

内存管理
// 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

  1. 迭代器失效:在修改列表时,迭代器可能会失效

    QList<int> list = {1, 2, 3, 4, 5};
    for (auto it = list.begin(); it != list.end(); ++it) {
        if (*it == 3) {
            list.erase(it); // 删除元素后,迭代器失效
            break; // 必须立即退出或重新获取迭代器
        }
    }
    
  2. 类型安全:QList是类型安全的,但要避免不安全的转换

    QList<QString> stringList = {"1", "2", "3"};
    // 错误:不能直接转换为int列表
    // QList<int> intList = stringList; 
    
    // 正确:显式转换
    QList<int> intList;
    for (const QString &str : stringList) {
        intList.append(str.toInt());
    }
    
  3. 性能考虑:对于大量数据,考虑使用更合适的容器

    // 如果需要频繁在中间插入删除,考虑QLinkedList(在Qt 5中)
    // 如果需要连续内存,考虑QVector(在Qt 5中)或std::vector
    

持续学习更新中。。。。。。。


网站公告

今日签到

点亮在社区的每一天
去签到