2.2 容器类

一.和C++的STL的常用容器类(list,deque,queue,array,vector,map)一样,Qt也定义了一些属于自己的容器类。

二.哪些Qt的数据类型可以放在Qt的容器中?哪些又不能放在Qt的容器中?

  2.1 基本数据类型(int,float,double,bool,long,..)这些基本数据类型都可以放在Qt的容器中

  2.2 一些Qt的常用数据类型(QString,QDate,QTime,..这些也可以放在容器中(因为这些类都重载了operator=和拷贝构造函数)

  2.3 一些基于QObject的类是不能放在Qt的容器中的(因为他们没有对拷贝构造函数和operator=进行重载或进行实现),比如QWidget,QDialog这些不能放在容器中

///下列的代码是错误的!
QDialog d1,d2,d3;
QList<QDialog>dialog_lst;//其实在这一步就已经错了
dialog_lst.append(d1);
dialog_lst.append(d2);
dialog_lst.append(d3);  

  2.4 但是如果我们非要存储基于QObject的派生类的对象怎么办?很简单,存他们的指针就行了,指针是可以存储的

三.QList,QLinckedList,QVector类的使用

  3.1 我们还是像上一节那样子新建一个Qt Console Application(Qt的控制台程序),然后我们在main.cpp中引入QList,QLinkedList,QVector这三个类以及QDebug类

    3.1.1 QList的使用  QList<QString>str_list;//定义一个QList容器对象,里面的元素类型是QString类型的变量    //定义一些字符串

    QString s1("ABCDEFGH");
    QString s2("123456");
    QString s3("Hello,World!");
    //然后我们将这些字符串装到容器中,由于QList重载了operator<<,因此可以以下面这种方式将元素插入到容器中
    str_list<<s1<<s2<<s3;
    //可以使用[]运算符将这些元素输出
    qDebug()<<str_list[0]<<str_list[1]<<str_list[2];
    //QList的一些成员方法(push_back,push_front,pop_back,pop_front)
    str_list.push_front(QString("ABSA"));//push_front:在列表前面插入数据
    str_list.push_back(QString("GDSAU"));//push_back:在列表尾插入元素
    str_list.pop_back();//删除尾部的一个元素(这个没有返回值哦!它并不会将删除的元素的一份拷贝返回给你)
    str_list.pop_front();//删除列表头的元素(同样没有返回值)
    qDebug()<<str_list.last();//返回列表的最后一个元素
    str_list.move(1,2);//将某个下标的元素移动到另一个下标的位置去
    //str_list.swap(1,2);//交换两个元素的值
    str_list.clear();//清空整个列表
    //str_list.erase();//删除某一个迭代器位置的元素(或者是迭代器范围内的元素)
  str_list.insert(2,QString("GDSA"));//按照位序插入元素
  str_list.append(QString("Last"));//往链表尾部插入元素
   ///使用迭代器来访问QList里面的元素 for(QList<QString>::iterator IT = str_list.begin();IT!=str_list.end();IT++) { qDebug()<<*IT; }

    3.1.2 QList的总结

      1.QList内部维护了一个指针数组(即这个数组里面的每一个元素都是指针类型),然后当我们插入变量实体时(非指针)时,它会将我们的实体变量拷贝一份存到链表中,然后他自己维护的那个指针指向这个空间(因此只要是能够插入到容器中的元素,如果本身是类类型的话,那么这个类一定是重载了operator=和拷贝构造函数的,如果没有这个条件,那么这种类类型的对象就不能插入容器中[只能插入其指针]=>比如Qt中一些继承与QObject类的它们都没有实现拷贝构造函数和operator=操作符,因此不能将他们的变量插入到容器中)

      2.QList通过分配连续的内存空间来存储元素(这也解释了为什么我们可以使用[]运算符进行元素的修改和访问),那么我们知道,内存中能够分配到很大的一块连续的内存空间是比较困难的,因此QList在存储元素特别多情况下,在其尾部进行插入是很浪费时间的,这个时候我们就可以用到链表(QLinkedList)

    3.1.3 QLinkedList的使用

   ///QLinkedList的使用
    QLinkedList<QString>str_linklist;
    str_linklist.push_front(QString("Hello"));
    str_linklist.push_back(QString(",World!"));
    str_linklist.append(QString("蜡笔小新Pointer"));
    //由于QLinkedList内部是使用的非连续的内存空间的分配方式,因此它不支持[]运算符来对元素进行访问,只能使用迭代器
    for(QLinkedList<QString>::iterator it = str_linklist.begin();it!=str_linklist.end();it++)
    {
        qDebug()<<*it;
    }
    //还可以使用反向迭代器
    for(QLinkedList<QString>::reverse_iterator r_it = str_linklist.rbegin();r_it!=str_linklist.rend();r_it++)
    {
        qDebug()<<*r_it;
    }

    3.1.4 QLinkedList总结

      1.QLinkedList底层分配的内存是不连续的,因此它无法像QList那样子使用[]运算符来随时通过下标访问到元素,它只能使用迭代器的方式来访问元素

      2.与QList相比,当数据的元素个数非常非常多时,采用QLinkedList会更加高效(第一,它使用的是非连续的内存空间,非连续的内存空间相比于一次性去分配很大一块连续的内存空间来说更为简单;第二,与QList相比,在数据元素都很多的情况下,QLinkedList插入元素的效率很高[因为它只能通过迭代器的方式来访问])

      3.QList和QLinkedList都不太适用于数据的查询,因为在链表中查询数据只能一个一个去查找遍历,效率很慢

  4.QVector类的使用

    和C++标准库中的vector一样,QVector是一种动态的数组容器,和array相比,它可以灵活的自由的扩展其长度,不像array那样必须在定义阶段就得规定好其最大长度

    QVector的优缺点:

      4.1 比array这种定长的数组相比,QVector拥有很好的扩展性,当容器容量不够用时,可以采用QVector来作为容器,因为它可以自动扩充容器的大小  

      4.2 随机访问的速度是很快的,因为QVector和vector一样都支持下标方式来访问数据

      4.3 缺点:当在QVector的首元素前面插入数据或在尾元素后面插入元素时,必然引起容器中元素的大量移动,这很费时间

///QVector的使用
    QVector<QString>str_vec;//初始化一个QVector容器,里面的元素都是QString类型
    //和C++的标准库的vector容器一样,QVector也支持push_front和push_back方法
    str_vec.push_front("Hello,World!");
    str_vec.push_back("蜡笔小新Pointer");
    str_vec.push_front("Hello!");
    str_vec.push_back("HeHe");
    //也支持append方法将元素插入到数组的尾部
    str_vec.append("World!");
    ///如何遍历出QVector里面的元素?(实际上有两种方式,一种是基于C++风格的迭代器,一种是基于Java风格的迭代器)
    ///1.C++风格的迭代器
    for(QVector<QString>::iterator it = str_vec.begin();it!=str_vec.end();it++)
    {
        qDebug()<<*it;
    }
    ///1.1 当然我们还可以使用C++11中的基于范围的for循环
    for(auto elem:str_vec)
    {
        qDebug()<<elem;
    }
    ///1.2 通过下标访问
    for(int i = 0;i<str_vec.size();i++)
    {
        qDebug()<<str_vec[i];
    }
    ///2.如果是使用Java风格的迭代器呢?
    QVectorIterator<QString>iter(str_vec);
    for(;iter.hasNext();)
    {
        qDebug()<<iter.next();
    }

  5.Qt中的迭代器

    迭代器顾名思义就是迭代容器的,迭代就是更新换代并且有遍历的含义在里面,在对Qt的容器所支持的迭代器中,分为两种迭代器,一种是基于Java风格的迭代器类,一种是基于C++风格的容器自身所具有的迭代器类

  5.1 Java风格的迭代器类

容器类 只读迭代器类 可读写迭代器类
QList<TYPE>,QQueue<TYPE> QListIterator<TYPE> QMutableListIterator<TYPE>
QLinkedList<TYPE> QLinkedListIterator<TYPE> QMutableLinkedListIterator<TYPE>
QVector<TYPE>,QStack<TYPE> QVectorIterator<TYPE> QMutableIterator<TYPE>

    注意:只读迭代器顾名思义是只能对容器进行只读操作,你不能在迭代的过程中去修改每一个迭代位置的元素的值哦!

    1.1 使用QListIterator<TYPE>类遍历QList<TYPE>类型的容器

   ///Java风格的迭代器
    QList<int>qli = {1,2,3,4,5,6,7,8,9,10};//我这里有一个QList<int>类型的容器
    //然后我定义一个只读迭代器去访问这个容器(注意:QList<TYPE>类型的容器只能用QListIterator<TYPE>类型的迭代器去遍历哦!
    QListIterator<int>i_iter(qli);//迭代器i_iter定义好了
    for(;i_iter.hasNext();)//使用for遍历
    {
        //i_iter.next() = 100;//如果我试图修改,那会报错,因为对于只读迭代器来说,它的next方法被const修饰了的,因此不能做修改操作
        qDebug()<<i_iter.next();
    }

   1.2 对于Java风格的迭代器(只读迭代器来讲,往往都会有以下方法可以使用)

    QListIterator<TYPE>::toBack();//将迭代器当前位置的指针移到最后一个容器的最后一个元素的后面

    QListIterator<TYPE>::hasPrevious();//看看当前迭代位置的前面是否还有元素

    QListIterator<TYPE>::previous();//返回当前迭代位置的前一个迭代位置的元素的值,并将当前迭代位置往前移一位

    QListIterator<TYPE>::toFront();//将迭代器位置指针移动到第一个元素的位置的前面

    QListIterator<TYPE>::peekNext();//返回下一个当前迭代器指针的下一个元素(但不移动迭代器指针)

    QListIterator<TYPE>::peekPrevious();//返回当前迭代器位置的前一个位置的元素(但不移动迭代器指针)

    QListIterator<TYPE>::findNext();//从当前迭代位置往后找指定的元素,如果有,则返回true,没有则返回false

    QListIterator<TYPE>::findPrevius();//从当前迭代位置往前面查找是否有指定的元素,如果有,则返回true,否则返回false

  1.3 可读写迭代器(这里我们还是以QList容器来作为测试容器,使用QMutableListIterator迭代器类来进行测试  

  QList<int>l_list = {1,2,3,4,5,6,7,8,9,10};//这里我还是定义了一个QList<int>类型的容器
    //然后我定义一个可读写迭代器QMutableQListIterator<int>
    QMutableListIterator<int>i_iter(l_list);
    //由于这个迭代器是可读写的,因此我可以对我的容器对象做任何的读写操作(写操作包括了添加元素,删除元素)
    i_iter.insert(11);//我往里面插入元素
    i_iter.insert(12);
    for(;i_iter.hasNext();)
    {
        qDebug()<<i_iter.next();
    }
    i_iter.toFront();
    i_iter.next();
    i_iter.remove();//删除一个元素

  1.4 可读写迭代器类相比于只读迭代器多了几个用于描述写权限的方法

    1.4.1 QMutableQListIterator<TYPE>::insert();//插入元素

    1.4.2 QMutableQListIterator<TYPE>::remove();//删除元素

 5.2 C++的STL风格的迭代器

    实际上这种STL的风格的迭代器也分为两种,一种是只读迭代器(即被const修饰的迭代器,一种是普通的可读写迭代器),具体的分类如下

容器类 只读迭代器类 可读写迭代器
QList<TYPE>,QQueue<TYPE> QList<TYPE>::const_iterator QList<TYPE>::iterator
QLinkedList<TYPE> QLinkedList<TYPE>::const_iterator QLinkedList<TYPE>::iterator
QVector<TYPE>,QStack<TYPE> QVector<TYPE>::const_iterator QVector<TYPE>::iterator

    5.2.1 我们还是通过QList来分别看一下只读迭代器和可读写迭代器的使用(C++STL风格的迭代器)    

  QList<int>l_list = {1,2,3,4,5,6,7,8,9,10};
    //SQL风格的迭代器(只读迭代器)
    QList<int>::const_iterator it = l_list.begin();
    for(;it!=l_list.end();it++)
    {
        qDebug()<<*it;
    }

    5.2.2 至于可读写迭代器,就是在定义迭代器对象时换为QList<int>::iterator it = l_list_begin();即可,这里不再赘述

6.QMap类和QHash类

  6.1 QMap和QHash类功能都是用来做查询的(都是通过键值对),而他们的区别仅仅在于:

  6.2 QMap底层是使用红黑树来实现的,因此往QMap里面插入的数据总是有序的;而QHash则是使用Hash表来实现的,当我们往Hash表中插入一对键值对时,内部会分配一个具体的Hash值来建立我这个键值对和这个Hash值的映射关系(每一个键值对对应的Hash值是唯一的),查询的时候,会通过具体的Hash值来查找,因此理论上QHash查询的速度比QMap更快

   6.3 QMap类及与QMap类相关的Java风格迭代器类及C++STL风格迭代器类

    Java风格的迭代器类(用于操作QMap容器和QMultiMap容器

容器类 只读迭代器类 读写迭代器类
QMap<KEY,VALUE>,QMultiMap<KEY,VALUE> QMapIterator<KEY,VALUE> QMutableMapIterator<KEY,VALUE>

    示例代码(使用QMapIterator访问QMap容器):

  QMap<QString,QString>m1;
    m1.insert("年龄","25");
    m1.insert("家庭住址","四川省成都市");
    m1.insert("籍贯","China");
    QMapIterator<QString,QString>m_iter(m1);
    for(;m_iter.hasNext();)
    {
        m_iter.next();//QMap容器和之前的QVector一样,必须要先next到第一个元素的位置,然后去取里面的键和值
        qDebug()<<m_iter.key() << ":" << m_iter.value();//拿到里迭代器的值里面的键和值
    }
  //同样的,对于这个QMapIterator类的迭代器只是一个只读迭代器,它不能对容器进行写操作,因此这个迭代器类里面没有

    示例代码(使用QMutableQMapIterator访问QMap容器)

QMap<QString,QString>m1;
    m1.insert("年龄","25");
    m1.insert("家庭住址","四川省成都市");
    m1.insert("籍贯","China");
    QMutableMapIterator<QString,QString>m_iter(m1);
    //这里我们做一个小例子,将我们的m1容器里面的键为"家庭住址"对应的值改为"北京市"
    for(;m_iter.hasNext();)
    {
        m_iter.next();
        if(m_iter.key()=="家庭住址")
        {
            m_iter.setValue("北京市");
        }
    }
    m_iter.toFront();
    for(;m_iter.hasNext();)
    {
        m_iter.next();
        qDebug()<<m_iter.key() << ":" << m_iter.value();
    }

   6.4 QHash容器的使用(前面我们使用的是Java风格的迭代器类,这里我们使用C++STL风格的迭代器)(只读迭代器和可读写迭代器)

   //Qt中QHash的使用
    //我们这里先使用可读写迭代器
    QHash<QString,QString>h_hash;
    h_hash.insert("Hello","World!");
    h_hash.insert("HeiHei","HuoHuo");
    h_hash.insert("姓名","太白金星");
    QHash<QString,QString>::iterator h_it = h_hash.begin();
    for(;h_it!=h_hash.end();h_it++)
    {
        if(h_it.key()=="姓名")
        {
            h_it.value() = "蜡笔小新Pointer";
        }
    }
    h_it = h_hash.begin();
    for(;h_it!=h_hash.end();h_it++)
    {
        qDebug()<< h_it.key() << ":" << h_it.value();
    }
  //Qt中QHash的使用
    QHash<QString,QString>h_hash;
    h_hash.insert("姓名","蜡笔小新Pointer");
    h_hash.insert("年龄","25");
    h_hash.insert("家庭住址","四川省成都市");
    //定义只读的迭代器对象
    QHash<QString,QString>::const_iterator it = h_hash.begin();
    for(;it!=h_hash.end();it++)
    {
        qDebug()<<it.key() << ":" << it.value();
        //it.key()="32";//由于我的it.key()函数被声明为了const属性,因此,不能修改键或值
    }

  

    

posted @ 2023-07-09 22:57  蜡笔小新Pointer  阅读(39)  评论(0)    收藏  举报