1.QLinkedList
QLinkedList
是Qt框架中的一个容器类,它实现了一个双向链表。Qt框架是一个跨平台的应用程序开发框架,广泛应用于GUI和非GUI程序的开发。QLinkedList
提供了一个高效的数据结构,用于存储和管理元素的集合。以下是对QLinkedList
的详细介绍:
1. 基本特性
- 动态大小:
QLinkedList
的大小是动态的,可以根据需要轻松添加或删除元素。这使得它在处理具有不确定大小的数据集时非常有用。 - 双向遍历:与
QVector
和QList
不同,QLinkedList
允许从头到尾以及从尾到头的双向遍历。这种遍历方式在某些场景下会非常有用。 - 高效的插入和删除操作:在链表的中间插入或删除元素的时间复杂度为O(1)。这使得
QLinkedList
在需要频繁插入和删除元素的场景中,具有优势。 - 不需要连续内存:
QLinkedList
的元素不需要存储在连续的内存空间中。这意味着在内存碎片化的情况下,QLinkedList
可能更容易分配内存。 - 模板类:
QLinkedList
是一个模板类,可以存储任意类型的元素。要使用QLinkedList
,需要包含<QLinkedList>
头文件,并用所需类型的模板参数实例化它。
2. 主要操作
- 创建和初始化:可以通过默认构造函数或初始化列表来创建和初始化
QLinkedList
。 - 插入元素:提供了
append
(在末尾添加元素)、prepend
(在开头添加元素)和insert
(在指定位置插入元素)等方法。 - 移除元素:支持移除指定位置的元素、移除特定值的元素以及清空链表等操作。
- 遍历元素:可以使用迭代器或范围循环来遍历
QLinkedList
中的元素。 - 其他常用方法:包括获取链表大小、检查链表是否为空、访问第一个和最后一个元素等。
3. 使用场景
- 频繁插入和删除:
QLinkedList
在中间插入和删除元素时非常高效,适合频繁进行这种操作的场景。 - FIFO队列:可以用
QLinkedList
来实现先进先出队列,使用prepend
添加元素,使用takeLast
提取元素。 - 需要双向遍历:由于
QLinkedList
是双向链表,可以在需要双向遍历的情况下使用。
4. 与其他容器类的对比
- 与QList对比:
QList
通常基于数组实现,支持常数时间复杂度的随机访问,但在中间或头部插入和删除时可能需要移动数组中的元素,时间复杂度为O(n)。而QLinkedList
插入和删除操作的时间复杂度为O(1),但随机访问效率较低,时间复杂度为O(n)。 - 与QVector对比:
QVector
也基于数组实现,与QList
类似,但它在某些情况下可能使用不同的内存分配策略。与QLinkedList
相比,QVector
的元素占据相邻内存空间,但在中间插入和删除元素时效率较低。
5. 注意事项
- 已弃用信息:值得注意的是,在最新的Qt版本中(如Qt 6),
QLinkedList
已经被标记为已弃用,并建议使用std::list
替代,因为它提供了类似的功能,并且与C++标准库更兼容。如果您的代码基已经使用了QLinkedList
,或者您有特定的理由需要使用它(例如,与老版本的Qt代码兼容),那么可以继续使用它,但建议针对新项目使用标准库中的容器。
综上所述,QLinkedList
是Qt框架中提供的一个双向链表容器类,它适用于频繁插入和删除操作的场景,并且支持双向遍历。然而,随着Qt版本的更新,它可能已被弃用,并建议使用标准库中的容器替代。
2.QLinkedList常用接口
QLinkedList
是Qt框架中的一个双向链表容器类,它提供了一系列丰富的接口用于元素的插入、删除、遍历和查询等操作。以下是一些常用的QLinkedList
接口:
1. 构造函数与析构函数
- 构造函数:
QLinkedList()
默认构造函数,创建一个空的链表。
2. 插入操作
- append(const T &value):在链表末尾添加一个元素。
- prepend(const T &value):在链表开头添加一个元素。
- insert(int i, const T &value):在链表的指定位置插入一个元素。位置索引从0开始,如果位置i大于链表的当前大小,则元素将被添加到链表的末尾。
3. 删除操作
- removeFirst():移除链表中的第一个元素并返回该元素。如果链表为空,则行为未定义。
- removeLast():移除链表中的最后一个元素并返回该元素。如果链表为空,则行为未定义。
- removeOne(const T &value):移除链表中第一个与指定值相等的元素,如果成功移除则返回true,否则返回false。
- removeAll(const T &value):移除链表中所有与指定值相等的元素,并返回移除的元素个数。
- clear():清空链表,移除链表中的所有元素。
4. 访问与遍历
- isEmpty() const:返回链表是否为空。
- first():返回链表中第一个元素的引用。如果链表为空,则行为未定义。
- last():返回链表中最后一个元素的引用。如果链表为空,则行为未定义。
- const_iterator begin() const:返回指向链表第一个元素的常量迭代器。
- const_iterator end() const:返回指向链表末尾的常量迭代器。
- iterator begin():返回指向链表第一个元素的迭代器。
- iterator end():返回指向链表末尾的迭代器。
5. 查找与索引
- contains(const T &value) const:检查链表是否包含指定值的元素。
- count(const T &value) const:返回链表中指定值的元素个数。
- indexOf(const T &value, int from = 0) const:返回链表中指定值的第一个元素的索引,从位置from开始搜索。如果未找到则返回-1。
- lastIndexOf(const T &value, int from = -1) const:返回链表中指定值的最后一个元素的索引,从位置from开始搜索。如果from为-1,则从链表末尾开始搜索。如果未找到则返回-1。
6. 迭代器与分割器
- const_iterator cbegin() const:返回指向链表第一个元素的常量迭代器。
- const_iterator cend() const:返回指向链表末尾的常量迭代器。
- reverse_iterator rbegin():返回指向链表最后一个元素的反向迭代器。
- const_reverse_iterator rbegin() const:返回指向链表最后一个元素的常量反向迭代器。
注意事项
QLinkedList
中的元素访问(如first()
和last()
)在链表为空时可能会导致未定义行为,因此在调用这些函数之前最好先检查链表是否为空。QLinkedList
不是线程安全的,如果在多线程环境下使用,需要外部同步机制来保证线程安全。
此外,随着Qt框架的发展,某些接口或特性可能会发生变化,因此建议查阅最新的Qt文档以获取最准确的信息。
3.QLinkedList遍历
QLinkedList
提供了多种方式来遍历其中的元素。以下是遍历 QLinkedList
的几种常用方法:
1. 使用迭代器
QLinkedList
提供了迭代器和反向迭代器,允许你从头到尾或从尾到头遍历链表。
QLinkedList<int> list;
list.append(1);
list.append(2);
list.append(3);
// 使用迭代器从头到尾遍历
for (QLinkedList<int>::iterator it = list.begin(); it != list.end(); ++it) {
qDebug() << *it;
}
// 使用反向迭代器从尾到头遍历
for (QLinkedList<int>::reverse_iterator rit = list.rbegin(); rit != list.rend(); ++rit) {
qDebug() << *rit;
}
2. 使用范围循环(C++11 及以上版本)
如果你使用的是 C++11 或更高版本,你可以使用范围循环来更简洁地遍历 QLinkedList
。
QLinkedList<int> list = {1, 2, 3};
// 使用范围循环遍历
for (int value : list) {
qDebug() << value;
}
3. 使用 first()
和 next()
方法
你还可以使用 QLinkedList
提供的 first()
方法来获取链表的第一个元素,并使用 next()
方法来获取下一个元素。这种方法比较底层,需要手动管理迭代过程。
QLinkedList<int> list = {1, 2, 3};
QList<QLinkedList<int>::Node*> nodes;
nodes.append(list.first()); // 获取第一个节点的指针
// 遍历节点
while (!nodes.isEmpty()) {
QLinkedList<int>::Node* node = nodes.takeFirst(); // 取出第一个节点并移除
qDebug() << node->value; // 访问节点的值
if (node->next) {
nodes.append(node->next); // 如果有下一个节点,则添加到列表中
}
}
但是,请注意,直接访问 QLinkedList
的内部节点结构(如上面的 Node
)通常不是推荐的做法,因为这依赖于 Qt 的内部实现,并且可能会在未来的版本中发生变化。这里只是为了展示一种可能的遍历方法。
4. 使用索引
虽然 QLinkedList
不是一个随机访问容器,但你可以使用索引来遍历链表。不过,这种方法的时间复杂度是 O(n),因为每次访问索引都需要从头开始遍历链表。
QLinkedList<int> list = {1, 2, 3};
for (int i = 0; i < list.size(); ++i) {
qDebug() << list.at(i); // 使用索引访问元素
}
然而,这种方法并不高效,特别是在链表很长的情况下。通常,更推荐使用迭代器或范围循环来遍历 QLinkedList
。
注意事项
- 遍历
QLinkedList
时,请注意不要修改链表的结构(如添加或删除元素),除非你使用的是能够处理这种情况的迭代器(如 STL 中的std::list
提供的迭代器)。在 Qt 中,直接修改链表可能会导致迭代器失效。 - 如果你需要在遍历过程中修改链表,可以考虑先收集需要修改的元素的信息,然后在遍历结束后进行修改,或者使用其他数据结构来辅助处理。