遍历一个容器可以使用迭代器(iterators)来完成,迭代器提供了一个统一的方法来访问容器中的项目。

Qt的容器类提供了两种类型的迭代器:

  • Java风格迭代器
  • STL风格迭代器

如果只是想按顺序遍历一个容器中的项目,那么还可以使用Qt的:

  • foreach关键字

Java风格迭代器

Java风格迭代器在使用时比STL风格迭代器要方便很多,但是在性能上稍微弱于后者。对于每一个容器类,都有两个Java风格迭代器数据类型:

  • 一个提供只读访问
  • 一个提供读写访问

50 QT容器类遍历_迭代器

QList示例:

QList<QString> list;
list << "A" << "B" << "C" << "D";

QListIterator<QString> i(list); // 创建列表的只读迭代器,将list作为参数
qDebug() << "the forward is :";

while (i.hasNext()) // 正向遍历列表,结果为A,B,C,D
qDebug() << i.next();
qDebug() << "the backward is :";

while (i.hasPrevious()) // 反向遍历列表,结果为D,C,B,A
qDebug() << i.previous();

50 QT容器类遍历_操作符_02

50 QT容器类遍历_操作符_03

这里先创建了一个QList列表list,然后使用list作为参数创建了一个列表的只读迭代器。这时,迭代器指向列表的第一个项目的前面(这里是指向项目“A”的前面)。然后使用hasNext()函数来检查在该迭代器后面是否还有项目,如果还有项目,那么使用next()来跳过这个项目,next()函数会返回它所跳过的项目。当正向遍历结束后,迭代器会指向列表最后一个项目的后面,这时可以使用hasPrevious()和previous()来进行反向遍历。

可以看到,Java风格迭代器是指向项目之间的,而不是直接指向项目。

所以,迭代器或者指向容器的最前面,或者指向两个项目之间,或者指向容器的最后面。

QMap示例:

QMap<QString, QString> map;    
map.insert("Paris", "France");
map.insert("Guatemala City", "Guatemala");
map.insert("Mexico City", "Mexico");
map.insert("Moscow", "Russia");
QMapIterator<QString,QString> i(map);
while(i.hasNext()) {
i.next();
qDebug() << i.key() << " : " << i.value();
}
if(i.findPrevious("Mexico"))
qDebug() << “find ‘Mexico’”; // 向前查找键的值

这里在QMap中存储了一些(首都,国家)键值对,然后删除了包含以“City”字符串结尾的键的项目。对于QMap的遍历,可以先使用next()函数,然后再使用key()和value()来获取键和值的信息。

STL风格迭代器

STL风格迭代器兼容Qt和STL的通用算法(generic algorithms),而且在速度上进行了优化。对于每一个容器类,都有两个STL风格迭代器类型:一个提供了只读访问,另一个提供了读写访问。因为只读迭代器比读写迭代器要快很多,所以应尽可能使用只读迭代器。

50 QT容器类遍历_java_04

QList示例:

QList<QString> list;
list << "A" << "B" << "C" << "D";
QList<QString>::iterator i; // 使用读写迭代器
qDebug() << "the forward is :";
for (i = list.begin(); i != list.end(); ++i) {
*i = (*i).toLower(); // 使用QString的toLower()函数转换为小写
qDebug() << *i; // 结果为a,b,c,d
}

qDebug() << "the backward is :";
while (i != list.begin()) {
--i;
qDebug() << *i; // 结果为d,c,b,a
}

QList<QString>::const_iterator j; // 使用只读迭代器
qDebug() << "the forward is :";
for (j = list.constBegin(); j != list.constEnd(); ++j)
qDebug() << *j; // 结果为a,b,c,d

50 QT容器类遍历_操作符_05

STL风格迭代器的API模仿了数组的指针,例如,使用“++”操作符来向后移动迭代器使其指向下一个项目;使用“*”操作符返回迭代器指向的项目等。需要说明的是,不同于Java风格迭代器,STL风格迭代器是直接指向项目的。

其中一个容器的begin()函数返回了一个指向该容器中第一个项目的迭代器,end()函数也返回一个迭代器,但是这个迭代器指向该容器的最后一个项目的下一个假想的虚项目,end()标志着一个无效的位置,当列表为空时,begin()函数等价于end()函数。

在STL风格迭代器中“++”和“--”操作符即可以作为前缀(++i,--i)操作符,也可以作为后缀(i++,i--)操作符。当作为前缀时会先修改迭代器,然后返回修改后的迭代器的一个引用;当作为后缀时,在修改迭代器以前会对其进行复制,然后返回这个复制。如果在表达式中不会对返回值进行处理,那么最好使用前缀操作符(++i,--i),这样会更快一些。对于非const迭代器类型,使用一元操作符“*”获得的返回值可以用在赋值运算符的左侧。STL风格迭代器的常用API如下表所示。

50 QT容器类遍历_迭代器_06

QMap示例:

QMap<QString, int> map;
map.insert("one",1);
map.insert("two",2);
map.insert("three",3);
QMap<QString, int>::const_iterator p;
qDebug() << "the forward is :";
for (p = map.constBegin(); p != map.constEnd(); ++p)
qDebug() << p.key() << ":" << p.value();// 结果为(one,1),(three,3),(two,2)

这里创建了一个QMap,然后使用STL风格的只读迭代器对其进行了遍历,输出了其中所有项目的键和值 。

Foreach关键字

foreach是Qt向C++语言中添加的一个用来进行容器的顺序遍历的关键字,它使用预处理器来进行实施。 例如:

QList<QString> list;
list.insert(0, "A");
list.insert(1, "B");
list.insert(2, "C");
qDebug() <<"the list is :";
foreach (QString str,list) { // 从list中获取每一项
qDebug() << str; // 结果为A,B,C
}

QMap<QString,int> map;
map.insert("first", 1);
map.insert("second", 2);
map.insert("third", 3);
qDebug() << endl << "the map is :";
foreach (QString str, map.keys()) // 从map中获取每一个键
qDebug() << str << " : " << map.value(str);
// 输出键和对应的值,结果为(first,1),(second,2),(third,3)

For循环

QList<QString> list;
list.insert(0, "A");
list.insert(1, "B");
list.insert(2, "C");
qDebug() <<"the list is :";
for(int i=0; i<list.size(); i++)
{ // 从list中获取每一项
qDebug() << list[i]; // 结果为ABC
}