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();
相关推荐
haiyaoyouyou3 小时前
Qt ElaWidgetTools 编译运行示例
开发语言·qt·qt creator·elaframework·mingw_64
不会写DN3 小时前
如何让两个Go程序远程调用?
开发语言·qt·golang
A.A呐18 小时前
【QT第三章】常用控件2
开发语言·qt
笨笨马甲18 小时前
Qt 实现三维坐标系的方法
开发语言·qt
谁动了我的代码?19 小时前
VNC中使用QT的GDB调试,触发断点时与界面窗口交互导致整个VNC冻结
开发语言·qt·svn
肖恭伟19 小时前
QtCreator Linux ubuntu24.04问题集合
linux·windows·qt
vegetablesssss20 小时前
QT国际化翻译
qt
困死,根本不会21 小时前
Qt Designer 基础操作学习笔记
开发语言·笔记·qt·学习·microsoft
喜欢喝果茶.21 小时前
Qt MQTT部署
开发语言·qt