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



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

Java 类型迭代器

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

表 1 Java类型的迭代器类
容器类 只读迭代器 读写迭代器
QList<T>, QQueue<T>  QListItcrator<T> QMutableListItcrator<T>
QLinkedList<T> QLinkedListIterator<T> QMutableLinkedListIterator<T>
QVector<T>, QStack<T> QVectorllcrator<T> QMutableVectorIterator<T>
QSet<T> QSetItcrator<T> QMutableSetItcrator<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 为例介绍迭代器的用法。

顺序容器类的迭代器的使用

Java 类型迭代器的指针不是指向一个数据项,而是在数据项之间,迭代器指针位置示意图如图 2 所示。





图 2 Java类型迭代器位置示意图

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

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

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



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

  1. QListIterator<QString> i (list);
  2. i.toBack();
  3. while (i.hasPrevious())
  4. qDebug() << i.previous();

QListItemtor 用于移动指针和读取数据的函数见表 3。

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

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

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

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

关联容器类的迭代器的使用

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



例如,下面的代码将删除键(城市名称)里以“City”结尾的数据项:

  1. QMap<QString, QString> map;
  2. map.insert("Paris", "France");
  3. map.insert("New York", "USA");
  4. map.insert("Mexico City", "USA");
  5. map.insert("Moscow", "Russia");
  6. ...
  7. QMutableMapIterator<QString, QString> i(map);
  8. while (i.hasNext ()) {
  9. if (i.next().key().endsWith("City"))
  10. i.remove();
  11. }

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

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

STL类型迭代器

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

表 4 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_iterator 和 reverse_iterator 定义相应的反尚迭代器。



STL 类型的迭代器是数组的指针,所以“++”运算符使迭代器指向下一个数据项,运算符返回数据项内容。与 Java 类型的迭代器不同,STL 迭代器直接指向数据项,STL 迭代器指向位置示意图如图 5 所示。





图 5 STL类型迭代器位置示意图

begin() 函数使迭代器指向容器的第一个数据项,end() 函数使迭代器指向一个虚拟的表示结尾的数据项,end() 表示的数据项是无效的,一般用作循环结束条件。



下面仍然以 QList 和 QMap 为例说明 STL 迭代器的用法,其他容器类迭代器的用法类似。

顺序容器类的迭代器的用法

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

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

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



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

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

关联容器类的迭代器的用法

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



例如,下面的代码将 QMap<int,int> map 中所有项的键和值输出:

  1. QMap<int, int> map;
  2. ...
  3. QMap<int, int>::const_iterator i;
  4. for (i = map.constBegin(); i != map.constEnd(); ++i)
  5. qDebug () << i.key () << ':' << i.value ();

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



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

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

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

而下面的代码是错误的:

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

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

Qt迭代器(Java类型和STL类型)详解的更多相关文章

  1. 【转】Java 类的生命周期详解

    一. 引 言 最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前国内java方面的教材大 ...

  2. Java类的生命周期详解

    引言 最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前国内java方面的教材大多只是告 ...

  3. Java中的枚举类型详解

    枚举类型介绍 枚举类型(Enumerated Type) 很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中.而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常量的定义 ...

  4. java类型和mysql类型的转换

    Integer  -----> int 11String     ----->  varchar 20Long       -----> bigint 20String      - ...

  5. java 基础类型和包装类的详解

    摘自:JAVA中基本类型的包装类 1. 包装类把基本类型数据转换为对象     每个基本类型在java.lang包中都有一个相应的包装类 2. 包装类有何作用     提供了一系列实用的方法     ...

  6. java中XMLGregorianCalendar类型和Date类型之间的相互转换

    import java.text.SimpleDateFormat;import java.util.Date;import java.util.GregorianCalendar;import ja ...

  7. Date类型和Long类型的相互转换

    Date类型和Long类型的相互转换: import java.text.SimpleDateFormat; import java.util.Date; public class T { publi ...

  8. Java之String类型详解

    字符串的特点 A:字符串一旦被赋值,就不能改变. 注意:这里指的是字符串的内容不能改变,而不是引用不能改变. B:字面值作为字符串对象和通过构造方法创建对象的不同 String s = new Str ...

  9. 着重基础之—MySql Blob类型和Text类型

    着重基础之—MySql Blob类型和Text类型 在经历了几个Java项目后,遇到了一些问题,在解决问题中体会到基础需要不断的回顾与巩固. 最近做的项目中,提供给接口调用方数据同步接口,传输的数据格 ...

随机推荐

  1. C语言:常用数学函数

    #include <stdio.h> #include <math.h> #include <stdlib.h> #include <time.h> # ...

  2. commons-collections利用链学习总结

    目录 1 基础 ConstantTransformer InvokeTransformer ChainedTransformer LazyMap TiedMapEntry TransformingCo ...

  3. 字典get方法和setdesault方法,统计message中各元素的出现频次

    message= 'There are moments in life when you miss someone so much that you just want to pick them fr ...

  4. 高性能内存图数据库RedisGraph(三)

    这篇文章,我将介绍截止目前,RedisGraph的最新版本(v2.4)对Cypher语言的支持情况. 1.模式(patterns) RedisGraph已支持Cypher中所有的模式. 2.类型(ty ...

  5. java 向Redis中存放数据 List<Device>转String

    /** * redis服务 */ @Autowired private RedisService redisService; //创建 Device  对象 Device no = new Devic ...

  6. 实时 + 高清 + 超压缩,阿里云视频云发布业内首款 VVC 编码器 Ali266

    基于新一代国际视频编解码标准 H.266/VVC,阿里云视频云近日发布了实时高清编码器 Ali266,有力推动 H.266/VVC 标准应用的落地,真正开启 H.266/VVC 的商用之路,并强力赋能 ...

  7. 使用JavaScript输出带有边框的乘法表

    在学习JavaScript(以下简称为js)过程中,会遇到输出9*9乘法表的问题,我们都知道利用双重for循环可以很简单的在网页中打印出来,可是你在做的过程中有没有想着给这个乘法表加一点花样呢? 下面 ...

  8. Aapache Tomcat AJP 文件包含漏洞(CVE-2020-1938)

    受影响版本 Apache Tomcat 6 Apache Tomcat 7 < 7.0.100 Apache Tomcat 8 < 8.5.51 Apache Tomcat 9 < ...

  9. 一些Shell脚本记录

    查看换行符 使用vim打开文件,输入:set ff?.根据返回结果可以文件类型 字符串 opcenter.sjb.bz [root@opcenter backup]# echo ${HOSTNAME% ...

  10. 3.python编程与计算机的关系,如何执行python文件

    上一节预告了这一章想讲如何不停地和世界打招呼,这涉及到编程中一个重要的概念:循环. 但经过了两周断更后细想了一下,不行,我们得对上一章进行补充,而且这个补充非常关键!也印证了上一章所说的: 上一节章很 ...