15、QT的容器类---------QT基础

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 提供的一个集合容器类,用于存储唯一的元素。与 QListQVector 不同,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();
相关推荐
ada0_ada15 小时前
qt模块学习记录
开发语言·qt·学习
knighthood20016 小时前
Qt5.15+VTK9.3.0实现点云点选功能
开发语言·qt
xyx-3v7 小时前
qt创建新工程
开发语言·c++·qt
大神的风范8 小时前
QT部署YOLO11实时检测
驱动开发·深度学习·qt·目标检测·计算机视觉
cpp_learner8 小时前
Linux ARM架构 使用 linuxdeployqt 打包QT程序
qt
泉飒9 小时前
C2001: 常量中有换行符-QT解决办法-逆向思路
开发语言·qt
泉飒10 小时前
QT的报错
qt
byxdaz10 小时前
QT中USB入门(QtUsb)
qt·qtusb
森G10 小时前
48、柱状图---------QChart
c++·qt
Larry_Yanan12 小时前
Qt+OpenCV(一)环境搭建
开发语言·c++·qt·opencv·学习