QT的容器类
和C++ 一样, QT也有自己的容器类, 包括顺序容器和关联容器
●顺序容器
○QList
○QVector
○QStack
○QQueue
●关联容器
○QMap
○QSet
○QMultiMap
○QHash
QList
列表初始化
QList为Qt独有的列表类型,支持任何位置的插入以及根据下标访问,同时支持迭代器操作,支持根据迭代器删除,根据下标删除
使用QList要包含头文件, 使用QList要指定存储的类型
cpp
//存储int类型的QList
QList<int> int_list;
//存储string类型的QList
QList<std::string> str_list;
QList可通过列表进行初始化构造
cpp
//通过列表初始化
QList<int> num_list = {0,1,2,3,4,5,6,7,8,9};
插入操作
QList插入操作,可在指定位置插入数据,也可在前后插入数据
cpp
//在指定位置前插入数据
void insert(int i, const T &value);
//在指定迭代器位置前插入数据
QList::iterator insert(QList::iterator before, const T &value);
//在尾部插入
void append(const T &value);
void push_back(const T &value);
//头部插入
void prepend(const T &value);
void push_front(const T &value);
练习:
1 初始化QList数据为{0,1,2,3,4,5,6,7,8,9},
2 在下标为2的元素之前插入数据100
3 并分别用prepend和push_back在尾部插入1024和2048,
4 然后用prepend和push_front在头部分别插入1023,2023
5 通过迭代器遍历在找到的第一个数据4之前插入22.
6 打印QList数据查看结果
提示:
QList迭代器操作和标准C++一样,可通过for,while等循环遍历
cpp
for(auto iter = num_list.begin(); iter != num_list.end(); iter++)
{
}
QT环境下可通过qDebug()输出日志信息,用法类似于std::cout,使用时要包含
cpp
#include <QDebug>
答案:
cpp
void list_insert(){
//存储int类型的QList
QList<int> int_list;
//存储string类型的QList
QList<std::string> str_list;
//通过列表初始化
QList<int> num_list = {0,1,2,3,4,5,6,7,8,9};
//在下标为2的元素前面
num_list.insert(2,100);
//在num_list尾部插入1024
num_list.append(1024);
//在num_list尾部插入2048
num_list.push_back(2048);
//在num_list头部插入1023
num_list.push_front(1023);
//在num_list头部插入2023
num_list.prepend(2023);
for(auto iter = num_list.begin(); iter != num_list.end(); iter++){
if(*iter == 4){
num_list.insert(iter,22);
break;
}
}
qDebug() << "num_list is " << num_list;
}
删除操作
erase操作可以删除某个迭代器所指向的元素, 删除后返回下一个元素的迭代器
cpp
QList::iterator erase(QList::iterator pos)
演示代码
cpp
void erase_demo(){
QList<int> num_list = {0,1,2,3};
//第一个元素的迭代器
auto first = num_list.begin();
//返回第二个元素的迭代器
auto second = num_list.erase(first);
qDebug()<< "second value is " << *second;
}
erase可以删除某个区间的元素,从begin迭代器开始,到end迭代器结束(不包括end),删除后返回end指向的元素的迭代器
cpp
QList::iterator erase(QList::iterator begin, QList::iterator end)
演示代码
cpp
void erase_between(){
QList<int> num_list = {0,1,2,3};
//第一个元素的迭代器
auto first = num_list.begin();
//迭代器偏移2个元素指向元素2
auto second = first+2;
//删除0~2的数据,不包括2
auto next_iter = num_list.erase(first, second);
//next_iter 指向的元素为2
qDebug()<< "next_iter value is " << *next_iter;
}
也可通过removeAt删除指定索引的元素
cpp
void removeAt(int i)
演示代码
cpp
void remove_demo(){
QList<int> num_list = {0,1,2,3};
//删除下标为1的元素
num_list.removeAt(1);
qDebug() << "num_list is " << num_list;
}
练习
1 初始化一个QList,数据为{0,1,2,3,4,5,6,7,8,9}
2 通过一次遍历删除其中的偶数元素
3 打印查看结果
答案:
cpp
void list_erase(){
//通过列表初始化
QList<int> num_list = {0,1,2,3,4,5,6,7,8,9};
for(auto iter = num_list.begin(); iter != num_list.end();){
//如果为偶数则删除,因为删除会自动返回下一个元素的迭代器
if(*iter % 2 == 0){
iter = num_list.erase(iter);
continue;
}
//如果为奇数,则让迭代器自增,指向下一个元素
iter++;
}
qDebug() << "num_list is " << num_list;
}
遍历方式
QT的容器也支持和C++ stl标准容器一样的遍历方式,除了迭代器方式,还可以通过C++ 11 的for写法
cpp
void list_forc11(){
QList<int> num_list = {0,1,2,3,4,5,6,7,8,9};
//c++11 方式遍历
for(auto & num: num_list){
qDebug() << num ;
}
}
QT提供了类似C++11的遍历方式foreach
cpp
void list_foreach(){
QList<int> num_list = {0,1,2,3,4,5,6,7,8,9};
//QT提供的foreach方式遍历
foreach(auto &num, num_list){
qDebug() << num ;
}
}
clear清空容器
Qt的容器都包含clear操作,和size操作
cpp
//清空容器
void clear();
//返回容器大小,返回的是存储元素的个数
int size() const
clear会清空容器存储的元素,但是容器所占用的空间不会被回收。
大家学习过std::vector,其中 包含size和capacity的概念,size表示vector中元素的个数,clear会清空vector,将size设置为0. 但是vector的capacity不会减少,也就是vector占用的空间不会减少。
我们以QList的清空为例
cpp
void list_clear(){
QList<int> list = {1,3,4,7,8,2};
//清空list
list.clear();
}
swap 操作
Qt的容器都包含swap操作,实现容器内部数据交换的功能。
cpp
void swap();
小技巧:
我们知道clear操作只能清空容器存储的数据,但是无法回收内存。
我们可以利用swap将我们要回收的容器L1和一个空的容器L2做交换,而空容器L2会因为交换存储L1的数据,L1变为空。
L2会在生命周期结束后自动析构回收内存。可以看看这个例子
cpp
void list_swap(){
QList<int> list = {1,3,4,7,8,2};
//定义一个空的list
QList<int> empty_list;
//将list和empty_list交换,list空间快速被释放。
list.swap(empty_list);
//empty_list会随着析构回收内存
}
QVector
QVector和std::vector用法类似,在QT6之后的版本QList和QVector功能上进行了合并,所以大家学会了QList就足够了。
还是看一下QVector用法当作复习
cpp
void vector_use(){
QVector<int> num_vec;
//向末尾插入元素100
num_vec.push_back(100);
//头部插入99
num_vec.push_front(99);
//向末尾插入元素为1024
num_vec.append(1024);
//删除头部元素
num_vec.pop_front();
//删除尾部元素
num_vec.pop_back();
//清空vector
num_vec.clear();
}
vector其余的用法和QList类似,不做赘述。
QStack
QStack顾名思义是栈容器,就是先进后出的容器。其继承自QVector,使用时需包含头文件

大家可以把栈容器想象成一个收纳箱,我们把要收纳的物品放到箱子里,取出的时候从上面取。
它的公有成员函数主要有四个
cpp
// 弹出栈顶元素
T pop()
// 元素t入栈
void push(const T &t)
// 交换两个栈元素
void swap(QStack<T> &other)
//返回栈顶元素(注意未弹出栈)
T & top()
//返回栈顶元素,重载版本返回常量引用
const T &top() const
//判断栈是否为空
bool empty();
练习:
有这样一组数字{6,1,0,5,2,9},要求:
1 将这组数列中每个元素在合适的时机入栈
2 栈顶元素出栈前打印栈顶元素,并且出栈
3 保证打印的数据顺序为从小到大
提示:
例如数列为 7,2,4,那么入栈顺序为7,2,打印栈顶元素为2,并弹出2,接着4入栈,打印栈顶元素为4,并弹出4,打印栈顶元素为7,并弹出7.
示意图

答案:
cpp
void stack_sort(){
QList<int> list = {6,1,0,5,2,9};
qDebug() << "利用栈顺序输出元素";
QStack<int> stack;
for(auto iter = list.begin(); iter != list.end(); iter++){
//判断如果栈为空则入栈
if(stack.empty()){
//qDebug() << "元素 " << *iter << " 入栈";
stack.push(*iter);
continue;
}
//判断如果要插入的元素比栈顶元素小则入栈
if(*iter < stack.top()){
//qDebug() << "元素 " << *iter << " 入栈";
stack.push(*iter);
continue;
}
//如果要插入的元素比栈顶元素大,则打印栈顶元素,并弹出栈顶元素,直到比栈顶元素小为止
while(!stack.empty() && *iter > stack.top()){
//元素比栈顶元素大则打印栈顶元素并出栈
qDebug() << "元素 " << stack.top() <<" 出栈";
stack.pop();
}
//qDebug() << "元素 " << *iter << " 入栈";
stack.push(*iter);
}
qDebug()<< "剩余栈元素";
//list遍历结束,可能栈中有元素,比如入list中存储的3,2,1
while(!stack.empty()){
qDebug() << "元素 " << stack.top() <<" 出栈";
stack.pop();
}
}
Queue
Queue是队列结构,其继承自QList,使用需包含,队列是先进先出的结构,例如我们排队等公交,站在队首的人优先出队并上车。

基本成员函数包括如下
cpp
// 从队首弹出元素
T dequeue()
// 将元素放入队尾
void enqueue(const T &t)
// 返回队首元素,不弹出
T & head()
// 返回队首元素,不弹出,返回值为常量引用
const T & head() const
// 交换两个队列内容
void swap(QQueue<T> &other)
// 判断队列为空
bool isEmpty() const
练习:
将一个数字序列{6,7,2,1,3,5,8,0,9}中的数字依次入队,全部入队后,依次出队,打印四次出队后的队列信息
QMap
QMap和C++的std::map用法类似,功能主要包括插入,删除,查找等。使用时需包含头文件
insert插入
将<key, value>插入map。
cpp
QMap::iterator insert(const Key &key, const T &value)
当map中存在某个key1值,对应的value值为value1,再次插入相同的key值<key1, value1> ,则将原来的value1更新为最新的value2。
案例:
cpp
void map_insert(){
//存储学生名字和年龄的map
QMap<QString, int> students_map;
//李雷今年23
students_map.insert("LiLei", 23);
//李雷年龄更新为24
students_map.insert("LiLei",24);
qDebug()<< students_map;
}
打印students_map可以看到map中仅有一个元素,key为LiLei,value为24.
cpp
QMap(("LiLei", 24))
insertMulti插入重复key
insertMulti支持插入的key为重复的<key,value>数据,如果key已经在map中存在,则额外再插入一条数据。
我们看下面的例子
cpp
void map_multi_insert(){
//存储学生名字和年龄的map
QMap<QString, int> students_map;
//李雷今年23
students_map.insertMulti("LiLei", 23);
//班里还有一个叫李雷的学生,今年24岁
students_map.insertMulti("LiLei",24);
qDebug()<< students_map;
}
我们打印students_map,可以看到存在两条数据,两条数据的key都是LiLei, 但是value不同
cpp
QMap(("LiLei", 24)("LiLei", 23))
判空操作
QMap的判空操作和Queue,QStack,QList等一样,都是通过isEmpty
cpp
// 判断map是否为空
bool isEmpty() const
查找操作
QMap 支持根据key查找map中的元素,如果找到返回找到元素的迭代器,如果未找到则返回无效的迭代器end
cpp
//根据key查找对应数据,返回迭代器
QMap::iterator find(const Key &key)
//根据key查找对应数据,返回常量迭代器,当map为常量时会调用这个重载版本
QMap::const_iterator find(const Key &key) const
例如,我们在QMap中插入一条数据<"LiLei", 23>, 先查找key为"LiLei"的数据,是可以找到的,如果查找key为"HanMeiMei",是无法找到的,find的返回值为一个无效的迭代器,指向map的end。
所以每次我们使用find之后要判断查找的返回的迭代器是否有效,有效才访问数据。
看下面这个例子
cpp
void map_find(){
QMap<QString, int> students_map;
//插入<"LiLei",23>数据
students_map.insert("LiLei",23);
//查找key为"LiLei"的数据
auto iter = students_map.find("LiLei");
if(iter != students_map.end()){
qDebug()<<"find key LiLei, value is " << iter.value();
}else{
qDebug()<<"find key LiLei failed ";
}
//查找key为"HanMeiMei"的数据
iter = students_map.find("HanMeiMei");
if(iter != students_map.end()){
qDebug()<<"find key HanMeiMei, value is " << iter.value();
}else{
qDebug()<<"find key HanMeiMei failed ";
}
}
上面的程序输出
cpp
find key LiLei, value is 23
find key HanMeiMei failed
重载的[]运算符
QMap和std::map一样,也重载了[]运算符,[]运算符的参数为要查找key,返回值为value的引用
cpp
// 根据key查找map返回
T & operator[](const Key &key)
const T operator[](const Key &key) const
注意
当key不存在的时候,会根据T的默认构造函数构造一个value插入到map中
代码示例
cpp
void map_operator_find(){
QMap<QString, int> students_map;
//根据key查找,返回value
auto value = students_map["Zack"];
qDebug() << "value is " << value;
qDebug() << "students_map is " << students_map;
}
上面的代码输出
cpp
value is 0
students_map is QMap(("Zack", 0))
获取值
我们知道使用重载的[]运算符,根据key获取value时,如果key不存在则会默认插入一条数据,这在一定情况下会产生误差。
我们可以通过value函数根据key获取value, 当key不存在的时候会返回一个value的默认值,如果value是基础类型则返回基础类型的默认值(int 为0, QString 为 "" 等),如果value为自定义的类类型,则调用默认构造函数返回。
我们可以为value函数第二个参数传递一个值,当key不存在的时候返回我们传递的值
cpp
const T value(const Key &key, const T &defaultValue = T()) const;
注意
当key不存在的时候不会向QMap中插入数据。
示例
cpp
void map_value(){
QMap<QString, int> students_map;
students_map.insert("Bob",29);
//根据key查找,返回value
auto zk_val = students_map.value("Zack");
qDebug() << "zack value is " << zk_val;
auto bob_val = students_map.value("Bob");
qDebug() << "apple value is " << bob_val;
auto lion_val = students_map.value("Lion",30);
qDebug() << "lion_val value is " << lion_val;
qDebug() << "students_map is " << students_map;
}
输出
cpp
zack value is 0
apple value is 29
lion_val value is 30
students_map is QMap(("Bob", 29))
contains
QMap提供了contains函数,用来判断是否存在某个键的记录
cpp
bool contains(const Key &key) const
示例代码
cpp
void map_contains(){
QMultiMap<QString, int> multiMap;
multiMap.insert("zack",25);
//判断是否存在
bool b_exist = multiMap.contains("zack");
if(b_exist){
qDebug()<< "map中存在key为zack的记录";
}else{
qDebug()<< "map中不存在key为zack的记录";
}
}
代码输出
cpp
map中存在key为zack的记录
删除操作
QMap的删除操作支持两种方式:
cpp
//根据迭代器删除,删除pos指向的元素
QMap::iterator erase(QMap::iterator pos);
//根据key值删除,删除map中键为key的数据
int remove(const Key &key)
示例代码
cpp
void map_del(){
QMap<QString, int> students_map;
//插入<"LiLei",23>数据
students_map.insert("LiLei",23);
//插入<"HanMeiMei",29>
students_map.insert("HanMeiMei",29);
//根据key删除,删除key为"HanMeiMei"的数据
students_map.remove("HanMeiMei");
//先查找key为"LiLei"的数据,返回迭代器
auto iter = students_map.find("LiLei");
//判断迭代器是否有效
if(iter == students_map.end()){
return;
}
//根据迭代器删除数据
students_map.erase(iter);
qDebug()<< "students_map is " << students_map;
}
上面的程序输出
cpp
students_map is QMap()
说明两个方式都可以删除数据。
练习:
统计一个字符串列表中,每个字符串出现的个数
字符串列表为{"Zack","Ella","Alice","Zack","Ella","Richard","Ella"}
要求将字符串和出现的个数存储到QMap中, 打印QMap数据
答案:
cpp
void calc_words(){
QList<QString> words = {"Zack","Ella","Alice","Zack",
"Ella","Richard","Ella"};
QMap<QString, int> word_map;
for(auto &word : words){
//根据[]运算符重载规则,如果word_map中不存在word,则value会被初始化为0
//如果存在则自增
word_map[word]++;
}
qDebug() << "word_map is " << word_map;
}
QMultiMap
QMultiMap 是 Qt 提供的一个多重映射容器类,它允许一个键对应多个值。与 QMap 类似,QMultiMap 也是基于平衡二叉树实现的,因此键值对是按键排序的。QMultiMap 的键和值都可以是任意类型,只要键类型支持比较操作。
插入键值对
QMultiMap插入相同的key不会修改之前的数据,会新增记录
cpp
void multi_map_insert(){
QMultiMap<QString, int> multiMap;
//插入键值对,key为apple
multiMap.insert("apple", 1);
//允许插入相同的key,
//不会修改之前apple的value,会新增一条
multiMap.insert("apple", 2);
//插入键值对
multiMap.insert("banana", 3);
qDebug() << "multimap is " << multiMap;
}
上面代码输出
cpp
multimap is QMap(("apple", 2)("apple", 1)("banana", 3))
访问值
QMultiMap支持返回指定key对应的所有value值,以列表形式返回
cpp
QList<T> values(const Key &key) const
代码示例
cpp
QList<int> appleValues = multiMap.values("apple"); // 返回 [1, 2]
int firstAppleValue = multiMap.value("apple"); // 返回 1(第一个值)
删除键值对
QMultiMap可通过remove操作删除所有建为key的键值对,返回删除的个数
cpp
int QMap<Key, T>::remove(const Key &akey)
案例
cpp
multiMap.remove("apple"); // 删除所有键为 "apple" 的项
也可以根据<key,value>键值对删除指定的项
cpp
int remove(const Key &key, const T &value);
案例
cpp
//删除key为apple,value为2的项
multiMap.remove("apple",2);
遍历QMultiMap
QMultiMap的遍历和QMap一样
cpp
QMultiMap<QString, int>::iterator it;
for (it = multiMap.begin(); it != multiMap.end(); ++it) {
qDebug() << it.key() << ": " << it.value();
}
QSet
QSet 是 Qt 提供的一个集合容器类,用于存储唯一的元素。与 QList 或 QVector 不同,QSet 不允许重复的元素,并且提供了高效的插入、删除和查找操作。QSet 的元素类型需要支持哈希函数和相等比较操作。使用时需包含 头文件
插入元素
通过insert将T类型的数据value插入集合
cpp
QSet::iterator insert(const T &value)
示例
cpp
// 定义一个 QSet
QSet<QString> set;
// 插入元素
set.insert("apple");
set.insert("banana");
还可以通过<< 操作符实现插入
示例
cpp
//插入cherry,date
set << "cherry" << "date";
判断元素是否存在
cpp
bool contains(const T &value) const
示例
cpp
// 检查元素是否存在
if (set.contains("apple")) {
qDebug() << "'apple' exists in the set.";
}
删除元素
删除元素和QMap类似,通过remove操作
cpp
bool remove(const T &value);
示例
cpp
// 删除元素
set.remove("banana");
遍历
我们可以通过迭代器遍历QSet中的元素
cpp
// 遍历 QSet
QSet<QString>::iterator it;
for (it = set.begin(); it != set.end(); ++it) {
qDebug() << *it;
}
QHash
QHash 是 Qt 提供的一个基于哈希表的数据容器类,用于存储键值对。与 QMap 类似,QHash 也允许通过键快速查找值,但 QHash 的查找、插入和删除操作的平均时间复杂度为 O(1),因此在处理大量数据时通常比 QMap 更高效。QHash 的键和值可以是任意类型,只要键类型支持哈希函数和相等比较操作。使用时需包含 头文件
插入键值对
QHash的插入操作和QMap一样,通过insert将key和value插入即可,当然也可以通过重载的[]进行插入
示例:
cpp
// 定义一个 QHash
QHash<QString, int> hash;
// 插入键值对
hash.insert("apple", 1);
hash["banana"] = 2;
hash.insert("cherry", 3);
访问值
QHash根据key获取value的方式和QMap一样
cpp
// 访问值
int appleValue = hash.value("apple");
qDebug() << "Value for 'apple':" << appleValue; // 输出 1
int bananaValue = hash["banana"];
qDebug() << "Value for 'banana':" << bananaValue; // 输出 2
判断和删除key
QHash判断key是否存在,以及删除key的操作,和QMap一样
示例
cpp
// 检查键是否存在
if (hash.contains("banana")) {
qDebug() << "'banana' exists in the hash.";
}
// 删除键值对
hash.remove("apple");
遍历
QHash同样支持迭代器遍历
cpp
// 遍历 QHash
QHash<QString, int>::iterator it;
for (it = hash.begin(); it != hash.end(); ++it) {
qDebug() << it.key() << ": " << it.value();
}
// 获取 QHash 的大小
qDebug() << "Size of hash:" << hash.size();