2021.6.1:QT——Qt迭代器(Java类型与STL类型)

迭代器为访问容器类中的数据提供了统一的方法,Qt有两种迭代器:Java类型的迭代器STL类型的迭代器

两者比较,Java迭代器更易于使用且提供了一些高级功能,而STL类型的迭代器效率更高。

Java类型迭代器

对每个容器类,有两个Java类型迭代器——一个用于只读操作,另一个用于读写操作,各个Java类型的容器类见下表

 

容器类只读迭代器读写迭代器
QList<T>, QQueue<T>  QListIterator<T> QMutableListIterator<T>
QLinkedList<T> QLinkedListIterator<T> QMutableLinkedListIterator<T>
QVector<T>, QStack<T> QVectorllcrator<T> QMutableVectorIterator<T>
QSet<T> QSetIterator<T> QMutableSetIterator<T>
QMap<Key, T>, QMultiMap<Key, T> QMapIterator<Key, T> QMutableMapIterator<Key, T>
QHash<Key, T>, QMultiHash<Key, T> QHashIterator<Key, T> QMutablcHashlterator<Key, T>

QMap和QHash等关联容器类的迭代器用法相同,QList和QLinkedList、QSet等容器类的用法相同,所以下面只以QMap和QList为例介绍迭代器的用法。

顺序容器的迭代器——QList

Java类型迭代器的指针并不是指向单个数据项,而是在数据项之间,迭代器指针位置示意图如下图:

 

 

 下面是遍历访问一个QList<QString>容器的所有数据项的典型代码:

QList<QString> list;
list<<"A"<<"B"<<"C"<<"D";
QListIterator<QString>i (list);
while (i.hasNext())
    qDebug() << i.next();

QList<QString>容器对象list作为参数传递给QListIterator<QString>迭代器i的构造函数,i用于对list作只读遍历。起始时刻,迭代器指针在容器第一个数据项之前(即上图的“A”之前),调用hasNext()判断在迭代器指针后面是否还有数据项,如果有,就调用next()跳过一个数据项,并且next()函数返回跳过的数据项

也可以反向遍历,示例代码如下:

QListIterator<QString> i (list);
i.toBack();
while(i.hasPrevious())
    qDebug() << i.previous();

QListIterator用于移动指针和读取数据的函数见下表:

 

函数名 功能
void toFront()  迭代器移动到列表的最前面(第一个数据项之前)
void toBack()   迭代器移动到列表的最后面(最后一个数据项之后)
bool hasNext()  如果迭代器不是位于列表最后位罝,返回true
const T& next() 返回下一个数据项,并且迭代器后移一个位置
const T& peekNext() 返回下一个数据项,但是不移动迭代器位置
bool hasPrevious() 如果迭代器不是位于列表的最前面,返回true
const T& previous()  返回前一个数据项,并且迭代器前移一个位置
const T& peekPrevious()  返回前一个数椐项,但是不移动迭代器指针

QListIterator是只读访问容器内数据项的迭代器,若要在遍历过程中对容器的数据进行修改,需要使用QMutableListIterator。例如下面的代码为删除容器中数据为奇数的项:

QList<int> list;
list<<1<<2<<3<<4<<5;
QMutableListIterator <int> i (list);
while(i.hasNext()){
    if(i.next()%2 != 0)
        i.remove();
}

remove()函数移除next()刚刚跳过的数据项,不会使迭代器失效。setValue()函数可以修改刚刚跳过去的数据项的值。

关联容器类的迭代器——QMap

对于关联容器类QMap<Key T>,使用QMapIteratorQMutableMapIterator迭代器类,它们具有上表所示的所有函数,只是增加了key()value()函数用于获取刚刚跳过的数据项的键和值。

例如,下面的代码将删除Key中以"City"结尾的数据项:

QMap<QString,QString> map;
map.insert("Paris", "France");
map.insert("New York", "USA");
map.insert("Mexico City", "USA");
map.insert("Moscow", "Russia");
...
QMutableMapIterator <QString,QString> i (map);
while (i.hasNext()){
    if(i.next().key().endsWith("City"))
        i.remove();
}

如果是在多值容器内遍历,可以用findNext()findPrevious()查找下一个或者上一个值,如下面的代码将删除上文map中value为"USA"的所有数据项:

QMutableMapIterator<QString,QString> i(map);
while(i.findNext("USA"))
    i.remove();

STL迭代器

STL迭代器与Qt和STL原生算法兼容,并且进行了速度优化。具体类型见下表:

 

容器类只读迭代器读写迭代器
QList<T>, QQueue<T> QList<T>::const_iterator QList<T>::iterator
QLinkedList<T> Q1. i nked List<1>: :const_iterator QLinkedList<T>::iterator
QVector<T>, QStack<T> QVector<T>::const_ilerator QVector<T>::iterator
QSet<T> QSet<T>::const_iterator QSet<T>::iterator
QMap<Key, P> QMultiMap<Kcy, T> QMap<Key, T>::const_iterator QMap<Key, T>:: iterator
QHash<Key, T> QMultiHash<Key, T> QHash<Key, T>: :const_iterator QHash<Key, T>::iterator

对每个容器类,同样也有两个STL迭代器——只读读写。无需修改数据时一定使用只读迭代器,因为速度更快。

注意,两种迭代器在定义时,使用了不同的关键字,const_iterator定义只读迭代器,iterator定义读写迭代器。此外,还可以使用const_reverse_iteratorreverse_iterator定义相应的反向迭代器

STL迭代器就是C++中的指针,所以"++"运算符使迭代器指向下一个数据项,运算符返回数据项内容。这就是与Java迭代器不同的地方,STL迭代器直接指向数据项

 

begin()函数使迭代器指向容器的第一个数据项,end()使迭代器指向最后一个数据项之后的空数据项,常用作迭代结束条件

下面仍以QList和QMap为例说明STL迭代器的用法。

顺序容器迭代器——QList

下面的代码将QList<QString> list中的数据项逐项输出:

QList<QString>list;
list<<"A"<<"B"<<"C"<<"D";
QList<QString>::const_iterator i;
for (i=list.constBegin();i!=list.constEnd();i++)
    qDebug()<<*i;

constBegin()constEnd()是用于只读迭代器的,表示起始结束位置

若使用反向迭代器,并将上面代码中的数据项都改为小写。代码如下:

QList<QString>::reverse_iterator i;
for(i = list.rbegin();i!=list.rend();i++)
    *i=i->toLower();

关联容器迭代器——QMap

对于关联容器类QMap和QHash,迭代器的操作符返回数据项的Value。如果想返回Key,使用key()函数,对应的,用value()函数一个项的value

例如,下面的代码将QMap<int,int>map中所有Key、Value输出:

QMap<int,int>map;
...
QMap<int,int>::const_iterator i;
for (i=map.constBegin();i!=map.constEnd();i++)
    qDebug()<<i.key()<<i.value();

Qt API包含很多返回值为QList或QStringList的函数,要遍历这些返回的容器,必须先复制。由于QT使用了隐式共享,所以这样的复制并无多大开销。

例如,下面的代码是正确的:

const QList<int> sizes = splitter->sizes();
QList<int>::const_iterator i;
for(i=sizes.begin();i!=sizes.end();i++)

而下面的代码是错误的:

QList<int>::const_iterator i;
for(i=splitter->sizes().begin();i!=splitter->sizes().end();i++)

 

说明:隐式共享对象的管理方法。一个对象被隐式共享,只是传递该对象的指针给使用者,而不复制对象数据,只有在使用者修改数据时,才实质复制共享对象给使用者。例如上边的代码中,splitter->sizes()返回的是一个QList<int>M表对象sizes,但是实际上并不传递内容,只是传递给它一个指针,只有当sizes发生数据修改时,才会将数据复制给sizes,这样避免了不必要的复制,减少了资源占用。

对于STL迭代器,隐式共享还涉及另一个问题,当一个迭代器在操作一个容器变量时,不要去复制这个容器变量。

 总结

  1. 迭代器是用于访问容器类中数据项的模板类;
  2. 有两种迭代器:JavaSTL迭代器;Java迭代器中,指针指向数据项之间STL迭代器中,指针指向数据项本身
  3. Java迭代器:以QList<T>为例,它的迭代器有QListIterator<T>QMutableListIterator<T>两种,分别是Read-OnlyRead&Write,其中QListIterator<T>用法:
    QList<QString> list;
    list<<"A"<<"B"<<"C"<<"D";
    QListIterator<QString> i (list);
    while(i.hasNext())
        qDebug()<<i.next();

    构建迭代器类型时,将QList对象作为参数传递给迭代器i构造函数,表示iQList对象的迭代器,只能用于这个QList对象的迭代。

  4. 上段代码中,i.hasNext()判断指针i之后是否有数据项i.next()跳过返回跳过数据项;此外,也有其它一些迭代方法,具体可在上文中查看;
  5. QMutableListIterator,读写迭代器,修改数据项的代码:
    QList <int> list;
    list<<1<<2<<3<<4<<5;
    QMutableIterator <int> i (list);
    while(i.hasNext())
        if(i.next()%2!=0)
            i.remove();

    修改数据项的函数是setValue()

  6. QMap类的迭代:
    QMap<QString,QString>map;
    ...
    QMutableMapIterator<QString,QString> i (map);
    while(i.hasNext())
        if(i.next().key().endsWith("City"))
            i.remove();
  7. STL迭代器:以QList<T>为例,也有两种迭代器:QList<T>::const_iteratorQList<T>::iterator,同样也是Read-Only、Read&Write
  8. STL迭代器就是C++中的指针,指向容器中的一个个数据项,可以使用“++”指向下一个数据项。list.constBegin(),list.constEnd()分别是Read-Only迭代器中指向容器类开始、结束数据项的函数。不过constEnd()并不是指结束项,而是结束项之后的空项,常用于迭代结束条件
  9. 读写迭代器中,list.rend()list.rbegin()分别指向结束开始
  10. QList<T>为例:
    QList<QString>list;
    list<<"A"<<"B"<<"C"<<"D";
    QList<QString>::const_iterator i;
    for(i=list.constBegin();i!=list.constEnd();i++)
        qDebug()<<*i;

    在初始化迭代器的时候,不需要用QList对象来作为它的构造函数的参数

  11. QMap为例:
    QMap<int,int>map;
    ...
    QMap<int,int>::const_iterator i;
    for (i=map.constBegin();i!=map.constEnd();i++)
        qDebug()<<i.key()<<i.value();
  12. Qt中有许多返回值QList的函数,要迭代这些返回的容器,需要先对容器复制。Qt使用了隐式共享,所以这样的复制并没有太大开销。
  13. 隐式共享——传递对象时,只传递指针,而不传递内容;
posted @ 2021-06-02 21:16  ShineLe  阅读(317)  评论(0)    收藏  举报