一、介绍

遍历一个容器可以使用迭代器(iterators)来完成,迭代器提供了一个统一的方法来访问容器中的项目。Qt的容器类提供了两种类型的迭代器:Java风格迭代器和STL风格迭代器。如果只是想按顺序遍历一个容器中的项目,那么还可以使用Qt的foreach关键字。

二、Java风格的迭代器

Java风格的迭代器在Qt4中加入,比STL风格的迭代器更易于使用,但是以轻微的效率作为代价,它们的API以Java的迭代器类为模型。

对于每个容器类,都有两种Java风格的迭代器类型:一种是只读,另一种是可读写。

容器 只读迭代器 可读写迭代器
QList<T>, QQueue<T> QListIterator<T> QMutableListIterator<T>
QLinkedList<T> QLinkedListIterator<T> QMutableLinkedListIterator<T>
QVector<T>, QStack<T> QVectorIterator<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> QMutableHashIterator<Key, T>

在这里,我们只关注QList和QMap。QLinkedList、QVector和QSet与QList的迭代器有同样的接口;QHash与QMap迭代器也有同样的接口。

与STL风格的迭代器不同,Java风格的迭代器指向项之间的位置,而不是直接指向项。由于这个原因,它们指向第一项之前,或者最后一项之后,或者两项之间。下面的图展示了包含4项的list的有效的迭代器位置,用红色箭头表示:

2.1 QList示例

(1)QList正序和倒序遍历

下面是一个典型的例子,迭代器按顺序循环正序遍历QList<QString>的所有元素,并把它们打印到控制台上:

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

流程是这样的:将要遍历的Qlist被传到QListIterator的构造函数,这时迭代器定位在list的第一项之前("A"之前),接下来我们调用hasNext()来检测迭代器后面是否有一项,如果有,我们调用next()来跳过那一项,next()函数返回它跳过的那一项。

下面讲解如何在QList中倒序遍历:

QListIterator<QString> i(list);

i.toBack();
while (i.hasPrevious())
qDebug() << i.previous();

代码和正序遍历是对称的,我们调用toBack()将迭代器移到最后一项后面的位置。下图描述了在一个迭代器上调用next()和previous()函数的效果:

(2)QList移除

QListIterator没有提供从list中插入或移除项的函数,想要实现插入和移除,你必须使用QMutableListIterator。下面举例说明使用QMutableListIterator从QList<int>中移除所有奇数。

QMutableListIterator<int> i(list);

while (i.hasNext())
{
if (i.next() % 2 != 0)
i.remove();
}

在倒序遍历中同样有效:

QMutableListIterator<int> i(list);

i.toBack();
while (i.hasPrevious())
{
if (i.previous() % 2 != 0)
i.remove();
}

(3)QList修改

如果想修改某项的值,我们可以使用setValue(),下面的代码中,我们用128来替换所以大于128的值:

QMutableListIterator<int> i(list);

while (i.hasNext())
{
if (i.next() > 128)
i.setValue(128);
}

下面的表概括了QListIterator的API:

函数 用途
toFront() 将迭代器移到list的最前面(在第一个项之前)
toBack() 将迭代器移到list的最后面 (最后一项之后)
hasNext() 如果迭代器没有到list的最后则返回true
next() 返回下一项,并将迭代器向前移动一个位置
peekNext() 返回下一项,不会移动迭代器
hasPrevious() 如果迭代器没有到list的最前面则返回true
previous() 返回上一项,并将迭代器移到上一个位置
peekPrevious() 返回上一项,不会移动迭代器

2.2 QMap示例

现在,我们来看看QMapIterator,有点不同,因为他在键值对上遍历。类似于QListIterator,QMapIterator提供了toFront()、toBack()、hasNext()、next()、peekNext()、hasPrevious()、previous()以及peekPrevious()。键和值的部分通过调用next()、peekNext()、previous()或peekPrevious()返回的对象的key()和value()来获得。

下面的例子中,移除所有首都名字以“City”结尾的一对(capital, country):

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

三、STL风格的迭代器

自从Qt2.0发布就可以使用STL风格的迭代器了,它们适用于Qt和STL的泛型算法,并且对速度作了优化。

对于每个容器类,有两种STL风格的迭代器类型:只读的和可读写的。尽可能使用只读的迭代器,因为它们比可读写的迭代器要快。

容器 只读迭代器 可读写的迭代器
QList<T>, QQueue<T> QList<T>::const_iterator QList<T>::iterator
QLinkedList<T> QLinkedList<T>::const_iterator QLinkedList<T>::iterator
QVector<T>, QStack<T> QVector<T>::const_iterator QVector<T>::iterator
QSet<T> QSet<T>::const_iterator QSet<T>::iterator
QMap<Key, T>, QMultiMap<Key, 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迭代器的API是以数组中的指针为模型的,比如++运算符将迭代器前移到下一项,*运算符返回迭代器所指的那一项。事实上,对于QVector和QStack,它们的项在内存中存储在相邻的位置,迭代器类型正是T *,const迭代器类型正是const T *。

在讨论中,我们重点放在QList和QMap,QLinkedList、QVector和QSet的迭代器类型与QList的迭代器有相同的接口;同样地,QHash的迭代器类型与QMap的迭代器有相同的接口。

3.1 QList示例

下面是一个典型例子,按顺序循环正序遍历QList<QString>中的所有元素,并将它们转为小写:

QList<QString> list;
list << "A" << "B" << "C" << "D"; QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
*i = (*i).toLower();

不同于Java风格的迭代器,STL风格的迭代器直接指向每一项。begin()函数返回指向容器中第一项的迭代器。end()函数返回指向容器中最后一项后面一个位置的迭代器,end()标记着一个无效的位置,不可以被解引用,主要用在循环的break条件。如果list是空的,begin()等于end(),所以我们永远不会执行循环。

下图展示了一个包含4个元素的vector的所有有效迭代器位置,用红色箭头标出:

倒序遍历需要我们在获得项之前减少迭代器,这需要一个while循环:

QList<QString> list;
list << "A" << "B" << "C" << "D"; QList<QString>::iterator i = list.end();
while (i != list.begin())
{
--i;
*i = (*i).toLower();
}

如果是只读的,你可以使用const_iterator、constBegin()和constEnd(),比如:

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

下面的表概括了STL风格迭代器的API:

表达式 用途
*i 返回当前项
++i 将迭代器指向下一项
i += n 迭代器向前移动n项
--i 将迭代器指向上一项
i -= n 将迭代器你向后移动n项
i - j 返回迭代器i和j之间项的数目

3.2 QMap示例

对于QMap和QHash,*运算符返回项的值,如果你想要获得键,只需在迭代器上调用key()。为了对称,迭代器类型还提供了value()函数来获得值。举个例子,下面是如何将QMap中的所有项打印到控制台上:

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

四、foreach关键字

如果你想要按顺序遍历容器中的所有项,你可以使用Qt的foreach关键字。这个关键字是Qt特有的,与C++语言无关,并且使用了预处理器实现。

它的语法是:foreach (variable, container) statement。比如,下面是如何使用foreach遍历QLinkedList<QString>:

QLinkedList<QString> list;
...
QString str;
foreach (str, list)
qDebug() << str;

在QMap和QHash中,foreach可以获得键值对中值的部分。如果你遍历既想获得键又想获得值,则可以使用迭代器(这样是最快的),或者你可以这样写:

QMap<QString, int> map;
...
foreach (const QString &str, map.keys())
qDebug() << str << ":" << map.value(str);

对于一个多值的(multi-valued)map:

QMultiMap<QString, int> map;
...
foreach (const QString &str, map.uniqueKeys())
{
foreach (int i, map.values(str))
qDebug() << str << ":" << i;
}

参考:

Qt——容器类(译)

Qt容器类之二:迭代器的更多相关文章

  1. Qt——容器类(译)

    注:本文是我对Qt官方文档的翻译,错误之处还请指正. 原文链接:Container Classes 介绍 Qt库提供了一套通用的基于模板的容器类,可以用这些类存储指定类型的项.比如,你需要一个大小可变 ...

  2. Qt容器类之一:Qt的容器类介绍

    一.介绍 Qt库提供了一套通用的基于模板的容器类,可以用这些类存储指定类型的项.比如,你需要一个大小可变的QString的数组,则使用QVector<QString>. 这些容器类比STL ...

  3. Qt容器类汇总说明

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt容器类汇总说明     本文地址:http://techieliang.com/2017/ ...

  4. Qt计算器开发(二):信号槽实现数学表达式合法性检查

    表达式的合法性 由于我们的计算器不是单步计算的,所以我们能够一次性输入一个长表达式.然而假设用户输入的长表达式不合法的话,那么就会引发灾难.所以有必要对于用户的输入做一个限制. 一些限制举例: 比方, ...

  5. QT学习(二)

    这一篇学习QT中最重要的也是最有特色的信号槽机制. (因为我学习过MFC,所以我觉得QT的信号槽机制和MFC中的消息响应机制是一一对应的.不过是MFC用的是宏来实现,而QT用的是消息和槽.相对来说QT ...

  6. Qt容器类——1. QList类、QLinkedList类和QVector类

    在开发一个较高性能需求的应用程序时,程序员会比较关注这些容器类的运行效率,表2.1列出了QList.QLinkedList和QVector容器的时间复杂度比较. 1.QList类 QList<T ...

  7. Qt容器类(总结)(新发现的QQueue和QStack,注意全都是泛型)

    Introduction Qt库提供了一组基于模板的一般化的容器类.这些容器可以存储指定的类型的元素.例如,如果你需要一个可变大小的Qstring数组,可以用QVector<QString> ...

  8. Qt容器类之三:通用算法

    在<QtAlgorithm>头文件中,Qt提供了一些全局的模板函数,这些函数是可以使用在容器上的十分常用的算法.我们可以在任何提供了STL风格迭代器的容器类上用这些算法,包括QList.Q ...

  9. Qt容器类的对象模型及应用(线性结构篇)(好多图,比较清楚)

    用Qt做过项目开发的人,肯定使用过诸如QList.QVector.QLinkList这样的模板容器类,它们虽然名字长的不同,但使用方法都大致相同, 因为其使用方法都大体相同,很多人可能随便拿一个容器类 ...

随机推荐

  1. 李洪强iOS开发之- 点击屏幕遮挡键盘

    李洪强iOS开发之- 点击屏幕遮挡键盘 实现的效果:  01 - 给当前的view添加点击事件,使点击屏幕的时候,让键盘退出 /** * 点击屏幕 隐藏键盘 * * @param tap */-(vo ...

  2. [网页游戏开发]容器的使用及自定义Tab,RadioGroup,List,ViewStack

    Morn里面,容器和其他普通组件不同,无需皮肤,所以也不能从组件树种拖动创建(Tab,RadioGroup例外),只能转换而来 Morn的容器组件主要有Box,Container,Panel,Tab, ...

  3. SpringMVC中返回JSON时乱码的解决方案

    springMVC中返回JSON会出现乱码,解决如下: produces = "text/html;charset=UTF-8" @ResponseBody @RequestMap ...

  4. VS 预先生成事件命令

    宏 说明 $(ConfigurationName) 当前项目配置的名称(例如,“Debug|Any CPU”). $(OutDir) 输出文件目录的路径,相对于项目目录.这解析为“输出目录”属性的值. ...

  5. 小程序登录方式切换 不做url跳转

    var filegUP = require('../../utils/getUserPassword.js'); var filemd5 = require('../../utils/md5.min. ...

  6. iframe 框架中 父子界面的JS调用

    子界面调用父界面 window.parent.hello(); 父界面调用子界面 window.frmaes[i].hello();

  7. Cats transport(codeforces311B)(斜率优化)

    \(Cats Transport\) 感觉这道题题面不好讲,就自翻了一个新的,希望有助于大家理解其思路: 大致题意: \(wch\) 的家里有 \(N\) 座山(山呈直线分布,第 \(i-1\) 座山 ...

  8. ABAP range 用法

    转自http://www.sapjx.com/abap-range-table.html 1. Range Table 概述 Range Table 为 SAP R/3系统标准内表的一种,结构与 Se ...

  9. bzoj4169: Lmc的游戏

    终于有道我会的了... int f[2][maxn],g[2][maxn],tot[maxn];//构造叶子编号时希望最大/小result 先手取子树最小/大的编号的排名 tot是子树中叶子个数 如果 ...

  10. Javascript中两种最通用的定义类的方法

    在Javascript中,一切都是对象,包括函数.在Javascript中并没有真正的类,不能像C#,PHP等语言中用 class xxx来定义.但Javascript中提供了一种折中的方案:把对象定 ...